自動的なサーバーのシャットダウン

自作の Linux サーバーを使っているので、最初から節電について考えました。以前使用していたパソコンを使っていますので、特定の NAS と異なって電力消費が少し多くなってしまいます。40 ワットぐらいを消費すると、24 時間の起動なら、(0.23 ユーロ/1kWhの電気価格だとすると)毎月 6.50 ユーロにも(900 円くらい)なります。パスコンがオフの時にサーバーが稼働している必要はないですから、どうやって節電できるでしょうか。

うちのサーバーは3台のパソコンと繋がっています。全部のパソコンがオフの時だけサーバーシャットダウンすべきです。そのために小さい Python のプログラムを開発しました。

やり方は相当簡単です。リストに書いてある IP アドレスを全てピングして、いずれかのパソコンから応答があると、スクリプトは直ちに終了します。もし応答がない場合でも、そのパソコンが再起動中の可能性がありますから、90 秒待機してからもう一度ピングします。最後にシャットダウンします。

で、それは一番基本的なやりかたです。段々もっと欲しい機能が思いついたので、それも開発してみました。

  • サーバーを起動しても、まだパソコンはオフの状態であったら、サーバーがすぐシャットダウンしてしまう時がよくありました。そのため、スクリプトが起動してからの時間(/proc/uptime に記載しています)を読み込んで、10 分間は必ず起動してからのみシャットダウンします。
  • たまに消えてほしくない時があります。それでファイルの中に起動すべきの分を書き込めます。そのファイルの変更の時間+中に書いてある時間を合わせたら、現在時間と比べます。例えば、そのファイルは 13:40 に変更して、その中に「120」を書き込んだら、13:40+120 分は 15:40 になります。現在時間は例えば 14:20 だとしたら、スクリプトがすぐ終了します。「touch」をつかって、簡単に現在時点から書けます。

最後にこのスクリプトを root として cron に入れなければなりません。

スクリプトは以下に記載しました。将来に Github にも提供する予定です。コメントが頂けたら、幸いです。

#!/usr/bin/env python3

#バージョン2
#2015年2月6日

import subprocess
import time
import os.path
from datetime import timedelta, datetime

ip_addresses = [ "192.168.0.2", #パソコン1
                "192.168.0.3", #パソコン2
                "192.168.0.4"] #パソコン3

shutdown_command = ["/usr/bin/systemctl", "suspend"]
do_not_shutdown_file = "/home/user/no-shutdown"
shutdown_only_after = timedelta(minutes=10)


def read_shutdown_file_time(dns_file):
    file_modify_date = datetime.fromtimestamp(os.path.getmtime(dns_file))
    with open(dns_file) as f:
        shutdown_after = timedelta(minutes=int(f.readline()))
    return file_modify_date + shutdown_after


def parse_uptime():
    with open("/proc/uptime", "r") as f:
        return timedelta(seconds=float(f.readline().split()[0]))


def main():
    try:
        if datetime.now() < read_shutdown_file_time(do_not_shutdown_file) or parse_uptime() < shutdown_only_after:
            exit()
    except FileNotFoundError as e:
        pass
    except ValueError as e:
        pass

    for computer in ip_addresses:
        if subprocess.call(["ping", "-c1", computer], stdout=subprocess.PIPE) == 0:
            exit()

    time.sleep(90)

    for computer in ip_addresses:
        if subprocess.call(["ping", "-c1", computer], stdout=subprocess.PIPE) == 0:
            exit()

    subprocess.call(shutdown_command)

if __name__ == "__main__":
    main()

コメント