プログラマーのメモ書き

伊勢在住のプログラマーが気になることを気ままにメモったブログです

OpenStreetMap のタイルサーバーの更新失敗時にメールを飛ばす

前から、OpenStreetMap のタイルサーバーを立てて、更新もできるようにして、エラーにもめげずに運用していました。

blog.mori-soft.com

blog.mori-soft.com

が、今日見てみたら、またエラーで更新が止まっていました。あーあ。

こちらのエラーの原因自体は、ググったらすぐにわかりまして、更新情報を取得しているURLが http のままだったためのようで、 https に変更したらすぐに動きました。

具体的には、設定ファイル /var/lib/mod_tile/.osmosis/configuration.txt

# The URL of the directory containing change files.
baseUrl=http://planet.openstreetmap.org/replication/hour

# Defines the maximum time interval in seconds to download in a single invocation.
# Setting to 0 disables this feature.
#maxInterval = 3600
maxInterval = 7200

にある baseUrl を http から https に修正するだけです。

ちなみにこのサーバー上だと、エラーはこんな感じででていました。

/var/log/tiles/osmosis.log

8 29, 2018 10:15:01 午後 org.openstreetmap.osmosis.core.Osmosis run
情報: Pipeline executing, waiting for completion.
8 29, 2018 10:15:02 午後 org.openstreetmap.osmosis.core.pipeline.common.ActiveTaskManager waitForCompletion
重大: Thread for task 1-read-replication-interval failed
org.openstreetmap.osmosis.core.OsmosisRuntimeException: The replication state doesn't contain a timestamp property.
        at org.openstreetmap.osmosis.replication.common.ReplicationState.loadProperty(ReplicationState.java:65)
        at org.openstreetmap.osmosis.replication.common.ReplicationState.load(ReplicationState.java:78)
        at org.openstreetmap.osmosis.replication.common.ReplicationState.(ReplicationState.java:59)
        at org.openstreetmap.osmosis.replication.common.ServerStateReader.getServerState(ServerStateReader.java:108)
        at org.openstreetmap.osmosis.replication.common.ServerStateReader.getServerState(ServerStateReader.java:50)
        at org.openstreetmap.osmosis.replication.v0_6.BaseReplicationDownloader.runImpl(BaseReplicationDownloader.java:290)
        at org.openstreetmap.osmosis.replication.v0_6.BaseReplicationDownloader.run(BaseReplicationDownloader.java:383)
        at java.lang.Thread.run(Thread.java:748)
(後略)

まあ、こんな感じで、理由はいろいろあるにせよ、たまにエラーで更新が止まっているのを見ると、やはりエラー時にはメールか何かで通知が必要だとと思います。

ということで、更新失敗時にメールを飛ばす設定を行ってみました。

メールサーバーの導入

タイルサーバーを作ったとき、メールサーバーなんて入れてませんでしたので、改めて入れます。 方法としては、こちらの記事

blog.mori-soft.com

で試したように、 ssmtp と mailutil をインストールして、メールの送信だけ可能にします。

mor@map:~$ sudo apt-get install ssmtp
mor@map:~$ sudo apt-get install mailutils

sSMTP の設定(/etc/ssmtp/ssmtp.conf)

mor@map:/etc/ssmtp$ cat ssmtp.conf
#
# Config file for sSMTP sendmail
#
# The person who gets all mail for userids < 1000
# Make this empty to disable rewriting.
root=postmaster

# The place where the mail goes. The actual machine name is required no 
# MX records are consulted. Commonly mailhosts are named mail.domain.com
#mailhub=mail
mailhub=メールサーバー:ポート番号

# Where will the mail seem to come from?
#rewriteDomain=

# The full hostname
hostname=map

# Are users allowed to set their own From: address?
# YES - Allow the user to specify their own From: address
# NO - Use the system generated From: address
#FromLineOverride=YES

# added for SMTP auth, 2018/8/30
AuthUser=ユーザー名
AuthPass=パスワード
AuthMethod=cram-md5

mor@map:/etc/ssmtp$ 

テストします。

mor@map:~$ echo test | mail -s test メールアドレス

指定したメールアドレスに無事メールが届いていればOKです。

注意点

何度も設定しているのに、下記にひっかかりました。ご注意ください。

  • /etc/ssmtp/ssmtp.conf はメール送信者が読める必要があります。ですが、認証パスワードが生のまま書かれているので、パーミッションにはご注意ください。
  • /etc/ssmtp/ssmtp.conf のメールサーバが間違っていると mail コマンドでテスト送信した際に失敗します。ちなみに、コンソールから試したときは、単にfailed と出て失敗したり、何も応答がなく終了するのではなく、入力中のようになるけど、入力が終了できないような状態になりました(Ctrl+Zで一旦バックグラウンドにまわしてkillしました)。お気を付けください。
  • 今回設定していたサーバー(Ubuntu 16.04.5 LTS)の場合、メールの送信に成功しても、 /va/log/mail.log にメールの送信ログが出力されませんでした。強引に下記のように自分でファイルを用意してやったら、ログが残るようになりました。別のサーバーだと問題なかったのでちょっと不思議ですね。
mor@map:~$ cd /var/log
mor@map:/var/log$ touch mail.log
mor@map:/var/log$ sudo chown syslog:adm mail.log
mor@map:/var/log$ sudo chmod 640 mail.log
mor@map:/var/log$ service syslog restart

タイルサーバー更新失敗時にメールを飛ばす設定

元々の更新に使っているスクリプト( mod_tile/openstreetmap-tiles-update-expire )を見ると、エラーが起きた場合は、 m_error とうい関数を呼び出して終了しているようです。 なのですが、この関数が exit だけでスクリプトを終了してしまいます。

m_error()
{
    echo "[`date +"%Y-%m-%d %H:%M:%S"`] $$ [error] $1" >> "$RUNLOG"    m_info "resetting state"
    /bin/cp $WORKOSM_DIR/last.state.txt $WORKOSM_DIR/state.txt || true    rm "$CHANGE_FILE" || true
    rm "$EXPIRY_FILE.$$" || true
    rm "$LOCK_FILE"
    exit
}

なので、この最後を

m_error()
{
    (略)
    exit 1
}

として、終了ステータスを設定するように変更します。

次に、終了ステータスを見てメール送信をおこなうスクリプトを書きます。 例えば、 ~/bin/osm-tile-update-expire-w-mail という名前で保存しておきます。

#!/bin/sh
#
# do openstreetmap-tiles-update-expire with mail notification if update failed.
#
# 2018/8/30, Junichi MORI

ACCOUNT=osm

SCRIPT_DIR=/home/${ACCOUNT}/src/mod_tile/
SCRIPT=openstreetmap-tiles-update-expire

MAIL_TO=メールアドレス


${SCRIPT_DIR}/${SCRIPT} > /dev/null 2>&1
STS=$?
#echo "status is " $STS 
if [ ${STS} != 0 ]
then
  echo "osm update script failed. Please check log files on /var/log/tiles . Last exit code is " ${STS} | mail -s "error: osm update" ${MAIL_TO}
fi
テスト

上記で作ったスクリプトを呼んでみます。 最初に言った修正はまだ反映していない状態としておきます(エラーが起きる状態です)。 そうすると、上記のスクリプト実行時に、エラーが起きて、メールが通知されます。無事メールが確認できました。

cron に設定

テストで問題がなければ、 cron で定期的に呼び出すようにします。

osm@map:~/bin$ crontab -e
osm@map:~/bin$ crontab -l
# Edit this file to introduce tasks to be run by cron.
# 
# Each task to run has to be defined through a single line
# indicating with different fields when the task will be run
# and what command to run for the task
# 
# To define the time you can provide concrete values for
# minute (m), hour (h), day of month (dom), month (mon),
# and day of week (dow) or use '*' in these fields (for 'any').# 
# Notice that tasks will be started based on the cron's system
# daemon's notion of time and timezones.
# 
# Output of the crontab jobs (including errors) is sent through
# email to the user the crontab file belongs to (unless redirected).
# 
# For example, you can run a backup of all your user accounts
# at 5 a.m every week with:
# 0 5 * * 1 tar -zcf /var/backups/home.tgz /home/
# 
# For more information see the manual pages of crontab(5) and cron(8)
# 
# m h  dom mon dow   command
#6 */1 * * *  /home/osm/src/mod_tile/openstreetmap-tiles-update-expire > /dev/null 2>&1
6 */1 * * *  /home/osm/bin/osm-tile-update-expire-w-mail > /dev/null 2>&1
osm@map:~/bin$ 

これで OpenStreetMap のデータ更新に失敗したらすぐに通知がくるので、すぐ気づけると思います。