プログラマーのメモ書き

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

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.<init>(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 のデータ更新に失敗したらすぐに通知がくるので、すぐ気づけると思います。

pre タグを使った場合のリンク不具合について

はてなブログで記事を書いているときに気づいたのですが、preタグを使った場合、preタグの後ろのリンクが正しく生成されない場合があります。 (2018/8/24時点の話です。いつからこうなっていたかは定かではありません。あと、私の環境での話です。他の方の環境は知らないです。)

たとえば、

<pre>
test
</pre>

のようにpreタグをいれると

test

このpreタグの後ろではURLを入れても、fotolifeの画像をfotolife記法で書いても

https://blog.mori-soft.com

[f:id:junichim:20180824163634j:plain]

のように正しくリンクが解決されません(fotolife記法がそのまま表示されていると思います)。

ここで、 pre タグに data-unlink="" 属性をつけていれると

<pre data-unlink="">
test2
</pre>
test2

https://blog.mori-soft.com

f:id:junichim:20180824163634j:plain

のように正しく表示されます。

もともと SyntaxHighlighter を使っていたりするので、それが原因かもと思い、SyntaxHighlighter の jsファイルやCSSの読み込みを外しても現象は変わりませんでした。 あ、編集は Markdown モードでやってます。

もし、同じような現象が出てる方がいれば、何かの参考になればと思います。 ではでは。

Syntaxhighlighter V4 に YAML ブラシを追加する

こちらの記事を書いたとき、 Syntaxhighlighter V4 に YAML のブラシがないことに気づきました。 なので、YAMLのブラシを追加できないか調べてみた顛末をまとめておきます。

なお、はてなブログで Syntaxhighlighter v4 を使う話は、以前下記にまとめてますので、ご参考までに。

blog.mori-soft.com

YAML ブラシ

SyntaxHgihlighter に Brushes and Themes というページがあり、そこに公式・非公式のブラシ一覧が載ってます。 ここを見ると、非公式としてですが YAML のブラシがあるようです。

GitHub - ErikWegner/brush-yaml

ということで、まずは、これを使ってみようと思います。

YAML ブラシのビルド

YAML ブラシのページを見ると SyntaxHighlighter のビルドの方法に従って、ビルドしてくれとあります。

Building · syntaxhighlighter/syntaxhighlighter Wiki · GitHub

なので、以前 SyntaxHighlighter V4 をビルドしたのと同じように試してみます。

mor@DESKTOP-RLA4CF1:~/work/syntaxhighlighter.d$ git clone https://github.com/ErikWegner/brush-yaml.git
mor@DESKTOP-RLA4CF1:~/work/syntaxhighlighter.d$ cd syntaxhighlighter
mor@DESKTOP-RLA4CF1:~/work/syntaxhighlighter.d/syntaxhighlighter$ ./node_modules/gulp/bin/gulp.js build --brushes=../brush-yaml/brush.js --theme=default
[14:32:21] Failed to load external module @babel/register
[14:32:21] Requiring external module babel-register
[14:32:21] Using gulpfile ~/work/syntaxhighlighter.d/syntaxhighlighter/gulpfile.babel.js
[14:32:21] Starting 'build'...
[14:32:21] Unknown brush "../brush-yaml/brush.js".
mor@DESKTOP-RLA4CF1:~/work/syntaxhighlighter.d/syntaxhighlighter$

うまくいきません。 ちなみに --build=all の場合は問題なくビルドできます。

指定方法を変えてみます。絶対パス

mor@DESKTOP-RLA4CF1:~/work/syntaxhighlighter.d/syntaxhighlighter$ ./node_modules/gulp/bin/gulp.js build --brushes=/home/mor/work/syntaxhighlighter.d/brush-yaml/brush.js --theme=default
[14:34:07] Failed to load external module @babel/register
[14:34:07] Requiring external module babel-register
[14:34:08] Using gulpfile ~/work/syntaxhighlighter.d/syntaxhighlighter/gulpfile.babel.js
[14:34:08] Starting 'build'...
[14:34:08] Unknown brush "/home/mor/work/syntaxhighlighter.d/brush-yaml/brush.js".
mor@DESKTOP-RLA4CF1:~/work/syntaxhighlighter.d/syntaxhighlighter$

うまくいきません。 公式のブラシは repos ディレクトリ以下にあるようなので、リンクを貼って

mor@DESKTOP-RLA4CF1:~/work/syntaxhighlighter.d/syntaxhighlighter$ cd repos/
mor@DESKTOP-RLA4CF1:~/work/syntaxhighlighter.d/syntaxhighlighter/repos$ ln -s ../../brush-yaml .
mor@DESKTOP-RLA4CF1:~/work/syntaxhighlighter.d/syntaxhighlighter/repos$ cd ..
mor@DESKTOP-RLA4CF1:~/work/syntaxhighlighter.d/syntaxhighlighter$ ./node_modules/gulp/bin/gulp.js build --brushes=repos/brush-yaml/brush.js --theme=default
[14:36:18] Failed to load external module @babel/register
[14:36:18] Requiring external module babel-register
[14:36:19] Using gulpfile ~/work/syntaxhighlighter.d/syntaxhighlighter/gulpfile.babel.js
[14:36:19] Starting 'build'...
[14:36:19] Unknown brush "repos/brush-yaml/brush.js".
mor@DESKTOP-RLA4CF1:~/work/syntaxhighlighter.d/syntaxhighlighter$

やっぱりうまくいきません。

docker で syntaxhighlighter V4 をコンパイル

困ったなと思っていたら、ブラシの生成一式を docker にまとめてくれている方がいらっしゃいました。

github.com

以前の記事を書いた際に参考にした issues に docker のことを追記してくれてました。)

Building: loadReposFromCache(...).error is not a function · Issue #428 · syntaxhighlighter/syntaxhighlighter · GitHub

なので、これを試してみます。

[~/tmp] # docker pull crazymax/syntaxhighlighter
[~/tmp] # mkdir syntax
[~/tmp] # docker run -it --rm -v "$(pwd)/syntax:/syntaxhighlighter/dist" crazymax/syntaxhighlighter:latest

~/tmp/syntax というのがローカル側のディレクトリで、コンテナの /syntaxhighlighter/dist にマウントしています。

で、実行させると、

[~/tmp] # ls -l syntax/
total 880
-rw-r--r-- 1 admin administrators  94034 2018-08-24 12:10 index.html
-rw-r--r-- 1 admin administrators 285507 2018-08-24 12:10 syntaxhighlighter.js
-rw-r--r-- 1 admin administrators 407571 2018-08-24 12:10 syntaxhighlighter.js.map
-rw-r--r-- 1 admin administrators   9094 2018-08-24 12:09 theme-default.css
-rw-r--r-- 1 admin administrators   9264 2018-08-24 12:09 theme-django.css
-rw-r--r-- 1 admin administrators   9474 2018-08-24 12:09 theme-eclipse.css
-rw-r--r-- 1 admin administrators   9120 2018-08-24 12:09 theme-emacs.css
-rw-r--r-- 1 admin administrators   9197 2018-08-24 12:09 theme-fadetogrey.css
-rw-r--r-- 1 admin administrators   9118 2018-08-24 12:10 theme-mdultra.css
-rw-r--r-- 1 admin administrators   9141 2018-08-24 12:10 theme-midnight.css
-rw-r--r-- 1 admin administrators   9135 2018-08-24 12:10 theme-rdark.css
-rw-r--r-- 1 admin administrators  10151 2018-08-24 12:10 theme-swift.css
[~/tmp] # 

のように、syntaxhighlighter のファイルが生成されています。docker実行中の画面を見ると、yamlのブラシも指定されているのが分かります。

[03:10:40] Starting 'build'...
[03:10:50] Brushes: applescript, as3, base, bash, coldfusion, cpp, csharp, css, delphi, diff, erlang, groovy, haxe, java, javafx, javascript, perl, php, plain, powershell, python, ruby, sass, scala, sql, swift, tap, typescript, vb, xml, repos/brush-halcon/brush.js, repos/brush-IEC61131/brush.js, repos/brush-kotlin/brush.js, repos/brush-latex/brush.js, repos/brush-Makefile/brush.js, repos/brush-mel/brush.js, repos/brush-objective-c/brush.js, repos/brush-yaml/brush.js
[03:10:50] Hash: 6ed82d7c6ef7b999f01e

なんで自分で試したときはうまくいかなかったのだろうか?

はてなブログに反映

作成した js ファイルを差し替えます。 はてな側では syntaxhighlighter の指定で

<pre class="brush: yaml">

と書くと、

f:id:junichim:20180824145706p:plain

のように無事に、YAMLもハイライト表示されるようになりました。