Raspberry Pi

systemdから自動起動させる

こんにちは鹿せんべいです。

ラズパイでVPNサーバーを作成した際に、VPNサーバーの自動起動を行ったので、備忘的に残します。

環境

やりたいこと

ラズパイの起動時に自動的にsoftetherVPNサーバーを動作させる。具体的にはOS起動時に、自動的に起動しVPNサーバーとしてVPNセッションの接続を待機する状態になり、またOSがシャットダウンする際、自動的にシャットダウン処理が行われるようにしたい

実装の前に

init.d or systemd

OS起動時に、起動しシャットダウンされるまで動作するデーモンプロセスにはinit.dsystemdがある。(DebianやCentOS、Fedoraなどの場合)

どちらに設定するのか、という問題がある。

ちなみに、 softetherの公式ドキュメント(https://ja.softether.org/4-docs/1-manual/7/7.3)にはinit.dにスクリプトを登録している。

ではinit.dでいいじゃないかと思われるかもしれないが、systemdはプロセスを並行して実行できるように設計されたinitの代替として登場したデーモンである。

このHP(https://www.tecmint.com/systemd-replaces-init-in-linux/)が詳しい。

systemdの特徴として書かれているものをいくつかピックアップする。

Features of systemd
・Clean, stateforward and efficient design.
→クリーンでステートフォワードで効率的なデザイン
・Simpler boot process.
→よりシンプルな起動プロセス
・Concurrent and parallel processing at boot.
→起動時の同時並行処理
・Better API.
→より良いAPI
・Simple Unit Syntax.
→シンプルなユニットの構文
etc...

後から出てきたsystemdは、init.dの欠点であった起動時にデーモンの起動に失敗した場合のカーネルパニックなどを避けられるように設計されており、systemdを使用できる場合はsystemdをすべきだとのこと。

公式のドキュメントは無視して、systemdにデーモンプロセスを登録していく方針が決まった。なお、softetherがinit.dにデーモンプロセスを登録している理由は不明である。単純に当時の設計思想のままであるのか、systemdに何らかの問題があるのか。ともあれ、systemdを第一に、問題が発生するとinit.dに登録する。

実装

systemdにデーモンプロセスを登録していこう。まずはvpnserver.serviceのファイルを作成する。なお、VPN Server を /usr/local/vpnserverにインストールしていることが前提である。

ユニットファイル作成

$ sudo touch /usr/lib/systemd/system/vpnserver.service
$ sudo chmod 664 /usr/lib/systemd/system/vpnserver.service
[Unit]
Description=SoftEther VPN Server
After=network.target

[Service]
Type=forking
ExecStart=/usr/lib/systemd/system/vpnserver.sh
ExecStop=/usr/local/vpnserver/vpnserver stop
KillMode=process
Restart=on-failure

[Install]
WantedBy=multi-user.targe

シェルスクリプト作成

次に上記の7行目の/usr/lib/systemd/system/vpnserver.shのシェルスクリプトファイルを作成する。

#!/bin/bash

/usr/local/vpnserver/vpnserver start
#tap=`/sbin/ifconfig -a| awk '$1 ~ /^tap/ {print $1}'`
#brctl addif br0 $tap

色々調べたところ、tapデバイスをブリッジするために4,5行目を記載していたのだが、「すでにブリッジに登録されている」旨のエラーが発生したため、コメントアウトした。確認はしていないのだが、VPNserverのセットアップの段階で設定していた気がする。

この4,5行目を記載するために、shファイルを作成したのだが、結局コメントアウトしたので、何をしているのかわからないw(※startのスクリプトだけならvpnserver.serviceに書いた方が断然いい)

とりあえず、上記のような中身で進めることにする。

systemdに自動起動の登録を行う。

$ sudo systemctl enable vpnserver
Faild to enable unit: Invalid argument

エラーだ。素直にいかないのが憎たらしい。ここですんなり登録できたら、sudo systemctl list-unit-files | grep vpnのコマンドを実行しているところまで飛ばしてもらって大丈夫だ。

僕と同じ”Faild to enable unit: Invalid argument”エラーが出た場合は、以下のコマンドを実行することになる。

$ sudo ln -s /usr/lib/systemd/system/vpnserver.service /etc/systemd/system/multi-user.target.wants/

簡単にコマンドの解説をすると、
ln -s [file][folder]を実行することで、[folder]に[file]のシンボリックリンクを貼ることができる。

systemctl enableのコマンドは実質的には/etc/systemd/system/multi-user.target.wants/の下にシンボリックリンクを張っているだけのようなので、無理やり自分でシンボリックリンクを張りに行ったという訳。

いつも通りで恐縮だが、上記のコマンドを適当に真似して発生した事象に対してはいかなる責任も追いません。自己責任でね。

自動起動のunit-filesのstatusを確認する

無事にsystemctlのコマンドを実行し、登録ができたら、念のために確認しておく。以下のコマンドを入力し、enabledになっていればデーモンプロセスとして登録されている。

$ sudo systemctl list-unit-files | grep vpn
vpnserver.service                      enabled    

動作確認

では、実際にOSの再起動を行い自動的にプロセスが起動し、VPNのセッション待機状態になっているかを確かめよう。

rebootを実行する。

$ sudo reboot

再起動したら、以下のコマンドを入力し、ステータスを確認する。
Active: active (running)になっていると、起動に成功している。

$ sudo systemctl status vpnserver
● vpnserver.service - SoftEther VPN Server
Loaded: loaded (/usr/lib/systemd/system/vpnserver.service; enabled; vendor preset: enabled)
Active: active (running) since Sun 2020-05-31 13:55:54 JST; 4min 6s ago
Process: 670 ExecStart=/usr/lib/systemd/system/vpnserver.sh (code=exited, status=0/SUCCESS)
Main PID: 716 (vpnserver)
Tasks: 38 (limit: 4915)
Memory: 18.6M
CGroup: /system.slice/vpnserver.service
├─716 /usr/local/vpnserver/vpnserver execsvc
└─717 /usr/local/vpnserver/vpnserver execsvc

ちなみにここで起動に失敗している場合などは、journalctl -xeのコマンドを入力し、遡ると割と明確にエラーが表示されていることが多い。

例えば、そのようなファイルはありません。だったり、構文が誤っています。など原因箇所が特定できることが多い。

以上だ。