モヒカンメモ

髪色が定期的に変わることに定評のある(比較的)若者Webエンジニアの備忘録

Ubuntu 18.04.2 LTSへtracerouteコマンドをインストールする

Ubuntu 18環境でネットワーク経路を確認しようとtracerouteコマンドを使おうとしたら入ってなかったのでシュパッとインストールする

f:id:pinkumohikan:20190715172330p:plain
traceroute --version

tracerouteコマンドとは

特定のIPやホストへのネットワーク経路や疎通を確認するためのコマンド

詳しくは:

tech.nikkeibp.co.jp

Ubuntu 18.04.2 LTSにはデフォルトではインストールされていない

インフラ屋か、構築直後しか使わないし、しょうがないね

$ traceroute

Command 'traceroute' not found, but can be installed with:

sudo apt install inetutils-traceroute
sudo apt install traceroute

おや、二個勧められたぞ?

www.gnu.org

inetutils-tracerouteは、GNUのやつなので安心感がありそう

sourceforge.net

tracerouteは、少ない権限で動いたり何かモダンらしい (SOURCEFORGEのdescription読んだだけw)

inetutils-tracerouteとtraceroute、どちらを使うべきか?

ぱっと調べた感じではtracerouteがおすすめっぽい

理由は、それぞれで blog.pinkumohikan.com への経路を調べたところ、 inetutils-traceroute ではオプションいじいじしても目的ホストまでたどり着けなかったが traceroute を使えばすんなりたどり着けたため

この記事も参考にした:

j3iiifn.hatenablog.com

ということでtracerouteのパッケージをインストール

$ sudo apt install traceroute
Reading package lists... Done
Building dependency tree
Reading state information... Done
The following NEW packages will be installed:
  traceroute
0 upgraded, 1 newly installed, 0 to remove and 9 not upgraded.
Need to get 45.4 kB of archives.
After this operation, 152 kB of additional disk space will be used.
Get:1 http://nova.clouds.archive.ubuntu.com/ubuntu bionic/universe amd64 traceroute amd64 1:2.1.0-2 [45.4 kB]
Fetched 45.4 kB in 1s (35.1 kB/s)
Selecting previously unselected package traceroute.
(Reading database ... 175935 files and directories currently installed.)
Preparing to unpack .../traceroute_1%3a2.1.0-2_amd64.deb ...
Unpacking traceroute (1:2.1.0-2) ...
Processing triggers for man-db (2.8.3-2ubuntu0.1) ...
Setting up traceroute (1:2.1.0-2) ...
update-alternatives: using /usr/bin/traceroute.db to provide /usr/bin/traceroute (traceroute) in auto mode
update-alternatives: using /usr/bin/lft.db to provide /usr/bin/lft (lft) in auto mode
update-alternatives: using /usr/bin/traceproto.db to provide /usr/bin/traceproto (traceproto) in auto mode
update-alternatives: using /usr/sbin/tcptraceroute.db to provide /usr/sbin/tcptraceroute (tcptraceroute) in auto mode

すんなり入った

$ traceroute --version
Modern traceroute for Linux, version 2.1.0
Copyright (c) 2016  Dmitry Butskoy,   License: GPL v2 or any later

お試しで調査してみる

$ traceroute --icmp blog.pinkumohikan.com
You do not have enough privileges to use this traceroute method.
socket: Operation not permitted

おおん!? 少ない権限で動くとは何だったのか

$ sudo traceroute --icmp blog.pinkumohikan.com
traceroute to blog.pinkumohikan.com (13.115.18.61), 30 hops max, 60 byte packets
 1  xxx.g.tyo1.static.cnode.io (xxx.xxx.xxx.xxx)  0.918 ms  0.988 ms  1.096 ms
 2  g-o-4eb-a13-4-v-712.interq.or.jp (157.7.41.129)  6.723 ms  6.833 ms  6.961 ms
 3  unused-133-130-013-013.interq.or.jp (133.130.13.13)  0.754 ms  0.855 ms  0.965 ms
 4  unused-133-130-012-034.interq.or.jp (133.130.12.34)  0.856 ms  0.965 ms  1.029 ms
 5  16509.tyo.equinix.com (203.190.230.53)  0.600 ms  0.604 ms  0.604 ms
 6  * * *
 7  * * *
 8  54.239.52.93 (54.239.52.93)  2.063 ms  1.693 ms  1.560 ms
 9  52.95.30.38 (52.95.30.38)  0.560 ms  0.566 ms  0.651 ms
10  * * *
11  * * *
12  * * *
13  52.95.31.55 (52.95.31.55)  2.160 ms  2.135 ms  2.151 ms
14  52.95.31.173 (52.95.31.173)  1.720 ms  1.692 ms  1.691 ms
15  52.95.31.158 (52.95.31.158)  3.173 ms  3.199 ms  3.215 ms
16  52.95.31.72 (52.95.31.72)  2.948 ms  3.509 ms  3.500 ms
17  * * *
18  * * *
19  * * *
20  * * *
21  * * *
22  * * *
23  * * *
24  * * *
25  * * *
26  * * *
27  * * *
28  * * *
29  * * *
30  * * *

失敗しとるやんけw

ICMPだと無理っぽいけど、TCP SYNを使うモードを見つけたので試してみる

$ sudo traceroute --tcp blog.pinkumohikan.com
traceroute to blog.pinkumohikan.com (13.115.18.61), 30 hops max, 60 byte packets
 1  xxx.g.tyo1.static.cnode.io (xxx.xxx.xxx.xxx)  0.910 ms  1.024 ms  1.045 ms
 2  g-o-4eb-a13-4-v-712.interq.or.jp (157.7.41.129)  1.107 ms  1.326 ms  1.527 ms
 3  unused-133-130-013-013.interq.or.jp (133.130.13.13)  0.614 ms  0.630 ms  0.619 ms
 4  unused-133-130-012-034.interq.or.jp (133.130.12.34)  0.774 ms  0.717 ms  0.844 ms
 5  16509.tyo.equinix.com (203.190.230.53)  1.041 ms  1.033 ms  1.030 ms
 6  * * *
 7  * * *
 8  54.239.52.89 (54.239.52.89)  1.954 ms 54.239.52.103 (54.239.52.103)  9.649 ms 54.239.52.107 (54.239.52.107)  1.492 ms
 9  52.95.30.44 (52.95.30.44)  0.988 ms 52.95.30.48 (52.95.30.48)  0.996 ms 52.95.30.60 (52.95.30.60)  0.870 ms
10  * * *
11  * * *
12  * * *
13  52.95.31.19 (52.95.31.19)  2.169 ms 52.95.31.43 (52.95.31.43)  2.507 ms 52.95.31.47 (52.95.31.47)  2.097 ms
14  52.95.31.221 (52.95.31.221)  2.020 ms 52.95.31.183 (52.95.31.183)  2.802 ms 52.95.31.215 (52.95.31.215)  2.092 ms
15  52.95.31.168 (52.95.31.168)  2.189 ms 52.95.31.188 (52.95.31.188)  4.170 ms 52.95.31.172 (52.95.31.172)  3.172 ms
16  52.95.31.86 (52.95.31.86)  2.249 ms 52.95.31.72 (52.95.31.72)  3.035 ms  2.943 ms
17  27.0.0.158 (27.0.0.158)  2.431 ms * *
18  * * *
19  * * *
20  * * *
21  * * *
22  * * *
23  ec2-13-115-18-61.ap-northeast-1.compute.amazonaws.com (13.115.18.61)  1.688 ms  2.683 ms  2.617 ms
24  ec2-13-115-18-61.ap-northeast-1.compute.amazonaws.com (13.115.18.61)  1.945 ms  2.926 ms  2.171 ms

いけた 🎉

docker alpineイメージでtimezoneをAsia/Tokyoに設定する

なぜやるか

dockerで良く使われる軽量イメージalpineは、デフォルトtimezoneがUTCになっており、Asia/Tokyo (JST) に設定しないと日本時間から9時間ずれてしまう。

どうやるか

Dockerfileに下記のように記せばOK

FROM alpine:latest

RUN apk --no-cache add tzdata && \
    cp /usr/share/zoneinfo/Asia/Tokyo /etc/localtime && \
    apk del tzdata

するとほら、この通り

$ docker build . -t alpine-jst
Sending build context to Docker daemon  2.048kB
Step 1/2 : FROM alpine:latest
...
Successfully tagged alpine-jst:latest

$ docker run --rm alpine-jst date
Fri Jul 12 07:52:07 JST 2019

補足: なぜ && でつなげたり、tzdataを削除するのか

Dockerfileのベストプラクティスに従うと必然的にこうなる。

docs.docker.jp

  • cp /usr/share/zoneinfo/Asia/Tokyo /etc/localtime のあたり必要なものは既にコピー済みなのでもうパッケージとしてtzdataをインストールしておく理由がない
  • alpine使うくらいなので少しでもイメージサイズ小さくするために余計なものは積極的に削除する

開発用だとかお試し目的ぐらいならそこまでやらなくても良いと思う。

mysqldumpコマンドで "Unknown table 'COLUMN_STATISTICS' in information_schema (1109)" と怒られる原因と対策

エラーメッセージ

mysqldump コマンドでMySQL上のデータのバックアップを取ろうとしたとき、下記のようなエラーが出た。

mysqldump: Couldn't execute 'SELECT COLUMN_NAME, JSON_EXTRACT(HISTOGRAM, '$."number-of-buckets-specified"') FROM information_schema.COLUMN_STATISTICS WHERE SCHEMA_NAME = '$db_name' AND TABLE_NAME = '$table_name';': Unknown table 'COLUMN_STATISTICS' in information_schema (1109)

原因

MySQL 8以降に付属するmysqldumpでそれ以前のMySQL 5.7とかのサーバに対してダンプを実行したらこの問題が起こるらしい。

serverfault.com

MySQL 8以降ではオプティマイザがヒストグラム統計というものを考慮するようになったため、mysqldumpもdumpをloadする際にヒストグラム統計をリセットさせる目的でANALYZE TABLE文を自動生成しようとする。そのときinformation_schema.COLUMN_STATISTICSを参照するが、そのテーブルがあるのはMySQL 8.0以降なのでそれ以前のバージョンのMySQLだと怒られが発生する、という具合のようだ。

対策

この問題はmysqldumpコマンドに --skip-column-statistics というオプションを設定して、ANALYZE TABLE文の自動生成をやめさせることで回避できる。

$ mysqldump --version
mysqldump  Ver 8.0.16 for osx10.14 on x86_64 (Homebrew)

$ mysqldump --help
...
  --column-statistics Add an ANALYZE TABLE statement to regenerate any existing
                      column statistics.
                      (Defaults to on; use --skip-column-statistics to disable.)
...

このように、先のオプションをつけたら確かにエラーが出なくなった 。

$ mysqldump --skip-column-statistics --host ...

先の対策の副作用

dumpのloadした際にヒストグラム統計を更新しない副作用としては、dumpをloadした先がMySQL 5.7以下なら特に無く、MySQL 8.0以降なら分析系のクエリでオプティマイザの判断が少し狂うかもしれない、ということらしい (日本男児さんブログからの受け売り)。

nippondanji.blogspot.com

テーブル統計情報のヒストグラム MySQL 8.0では、オプティマイザがテーブル統計情報のヒストグラムを活用できるようになった。WHERE句の条件にマッチする行が、テーブルスキャンによってどの程度に絞り込まれるかということを見積もることによって、敢えてスキャンを選ぶべきかどうかが分かる。リアルタイム性が高い処理ではインデックスをバッチリ使用するように最適化することが多いのでヒストグラムの出番は無いだろうが、分析系の処理では重宝することもあるだろう。

参考にした資料

serverfault.com

dev.mysql.com

nippondanji.blogspot.com

yoku0825.blogspot.com

dev.mysql.com

Scalaで使えそうなメールライブラリの調査

Scalaでメール送りたくなってメールライブラリを探したのでまとめておく。

2019年になぜメールか

アプリのプッシュ通知やLINE@などの通知手段が普及した2019年でも、保存性やエビデンス、長文の書きやすさ、所有の証明といったの観点でメールという手段も無視出来ない。

探し方

以前テンプレートエンジンを探したときと観点は同じ。

blog.pinkumohikan.com

githubでそれっぽいキーワードで探して、

  • そこそこstarがついている (= みんなに使われている)
  • 定期的に新バージョンがリリースされている
  • contributorが一人じゃない

辺りの観点で、良さげなやつを見つける。

見つけたもの

ScalaにはEmail系のAPIは用意されてないっぽいので、Java用のライブラリを使う感じになる。軽く探して見つかったのは Apache Commons EmailSimple Java Mail の2つ。どちらもJava Mail APIのwrapperで、低レイヤー特有の難しい表現が減っていて使いやすそう。

1. Apache Commons Email

github.com

commons.apache.org

こんな感じに使うっぽい

val email = new SimpleEmail();
email.setHostName("smtp.googlemail.com");
email.setSmtpPort(465);
email.setAuthenticator(new DefaultAuthenticator("username", "password"));
email.setSSLOnConnect(true);
email.setFrom("user@gmail.com");
email.setSubject("TestMail");
email.setMsg("This is a test mail ... :-)");
email.addTo("foo@bar.com");
email.send();

Java感が強いな

特徴

  • HTML, TEXTメール両方対応
  • Apacheが出してるので安心感がある
  • Simple Java Mailよりもcontributor多い
  • SimpleEmailオブジェクトに送信設定とかメールそのものをガバっと持たせるスタイル
  • Apache-2.0ライセンス

2. Simple Java Mail

github.com

www.simplejavamail.org

こんな感じに使うっぽい

val email = EmailBuilder.startingBlank()
          .to("lollypop", "lolly.pop@somemail.com")
          .withSubject("hey")
          .withPlainText("Please view this email in a modern email client!")
          .buildEmail();

val mailer = MailerBuilder
          .withSMTPServer("smtp.host.com", 587, "user@host.com", "password")
          .withTransportStrategy(TransportStrategy.SMTP_TLS)
          .buildMailer();

mailer.sendMail(email);

特徴

  • HTML, TEXTメール両方対応
  • Apache Commons EmailよりもStarが多い
  • ドキュメントが読みやすい印象
  • MailerとEmailオブジェクトで別れていて、MailerへEmailを渡すスタイル
  • Apache-2.0ライセンス

個人的には、送信関連の設定はMailerに、emailそのものの関心はEmailに持たせるという関心の分離が出来ているSimple Java Mailのほうが好み。

自宅からメールが送れない?それOP25Bのせいかも

メールを送るシステムを開発していて、会社のネットワークからはメールが送れるのに、自宅やポケットWiFiからだとメールが送れないことがあります。例えば、メールを送ろうとすると No route to hostDestination host unreachable Request timed out が起きる場合です。

実は多くのISPでは野良メール送信を制限するために外向きのTCP 25番を閉じています。このせいで、自宅PCから会社メールサーバを使ってメールを送ろうとしたり、自前メールサーバからのメールが送れなかったりします。

OP25B (Outbound Port 25 Blocking)

電子メールは技術的にはTCP 25番で相手先のメールサーバにSMTPでお話すると誰でも送ることができます。2010年頃、メールの添付ファイルで感染するウイルスが大流行したり、SPAMメールの社会問題化がきっかけでOP25Bが始まりました。

www.so-net.ne.jp

ISPからしてみれば

  • TCP 25番を閉じることで、ユーザ端末から直でSPAMメールやウイルスメールを送られることを防げる
  • 別ポートでメール送信用サーバを用意してユーザに使わせれば、これまで通りメールサービスは提供できる

わけですね。

一見、「ISPを経由すればSPAM送れるから一緒じゃね?」って思いそうですが、ISPのメールサーバに記録が残るので通報があった際に追跡しやすいし、メールサーバにウイルス対策ソフトを入れることで早期にウイルス感染を検出して直接ユーザに連絡を入れること可能になるので大きな違いなわけです。

どうすればメールが送れるか?

利用するメールサーバがサブミッションポート (TCP 587番) を提供している場合

メール送信時のメールサーバのポートをTCP 25番の代わりにTCP 587番を使う設定にすると、メールを送れるようになります。

$ git diff
diff --git a/.env.default b/.env.default
index e08cad1..f5d901a 100644
--- a/.env.default
+++ b/.env.default
@@ -1,2 +1,2 @@
 MAIL_HOST=mail.pinkumohikan.om
-MAIL_PORT=25
+MAIL_PORT=587

利用するメールサーバがサブミッションポート (TCP 587番) を提供していない場合

端的に、そのメールサーバを使ってメールを送ることはできません。

僕はだいたいそういう場合、Gmailアカウントを取得してGmailの提供しているメールサーバ (Gmail SMTP サーバー) を使ったりします。そもそもTCP 25番を提供していないというのがトラップを未然に防いでいてポイント高い。

support.google.com

ちなみに、ご存知の通りGoogleはセキュリティに厳しいのでVPSとかからメール送ろうとすると不審なリクエストと見なされてrejectされることもよくある。送信数の制限も厳しいので、本番用途では使わないこと。

自前メールサーバを使いたい場合

OP25Bの影響で相手先メールサーバとの直接通信ができないので、ISPの提供するメールサーバなどに代理でメールを送ってもらう必要があります。

自宅メールサーバ -> (TCP 25番) -> 相手先メールサーバ

という感じの構図から、

自宅メールサーバ -> (TCP 587番) -> ISPメールサーバ -> (TCP 25番) -> 相手先メールサーバ

という構図になるイメージです。

詳細な設定方法は割愛しますが、postfixだったら relayhost あたりの設定がキーワードです。

MySQLに投げられたすべてのSQLクエリをロギングする

概要

クエリビルダやORMが生成するクエリを確認したいなどの理由で、MySQLに投げられたクエリを確認したくなることがある

そういうときは、MySQLのクエリログ (general_log) と言う設定をONにすることによって、すべてのクエリをログに吐かせることができる

dev.mysql.com

5.2.3 一般クエリーログ 一般クエリーログは、mysqld の実行内容の一般的な記録です。サーバーは、クライアントが接続または接続解除したときに情報をこのログに書き込み、クライアントから受け取った各 SQL ステートメントをログに記録します。一般クエリーログは、クライアント側でエラーが疑われるとき、クライアントが mysqld に送信した内容を正確に知りたい場合に非常に役立つことがあります。

やってみる

1. 現在の設定を確認する

mysql> select version();
+-----------+
| version() |
+-----------+
| 8.0.16    |
+-----------+
1 row in set (0.00 sec)

mysql> show variables like 'general_log%';
+------------------+---------------------------------+
| Variable_name    | Value                           |
+------------------+---------------------------------+
| general_log      | OFF                             |
| general_log_file | /var/lib/mysql/3a9a8fa16e7a.log |
+------------------+---------------------------------+
2 rows in set (0.00 sec)

デフォルトではOFFになっている。この状態で クエリが投げられてもロギングされない

root@3a9a8fa16e7a:/# ls -alh /var/lib/mysql/3a9a8fa16e7a.log
ls: cannot access '/var/lib/mysql/3a9a8fa16e7a.log': No such file or directory

2. ONにしてみる

mysql> set global general_log = on;
ERROR 1227 (42000): Access denied; you need (at least one of) the SUPER or SYSTEM_VARIABLES_ADMIN privilege(s) for this operation

このユーザには権限がなかったので、グローバル設定を書き換える権限のあるユーザでログインしなおして再チャレンジ

mysql> set global general_log = on;
Query OK, 0 rows affected (0.02 sec)

mysql> show variables like 'general_log%';
+------------------+---------------------------------+
| Variable_name    | Value                           |
+------------------+---------------------------------+
| general_log      | ON                              |
| general_log_file | /var/lib/mysql/3a9a8fa16e7a.log |
+------------------+---------------------------------+
2 rows in set (0.01 sec)

クエリログの設定をONにしたところ即座にログファイルが作られた

root@3a9a8fa16e7a:/# ls -alh /var/lib/mysql/3a9a8fa16e7a.log
-rw-r----- 1 root root 256  5月 25 17:17 /var/lib/mysql/3a9a8fa16e7a.log

3. ロギングされるか確かめる

適当にクエリを投げてみる

mysql> select 1;
+---+
| 1 |
+---+
| 1 |
+---+
1 row in set (0.01 sec)
root@3a9a8fa16e7a:/# tail /var/lib/mysql/3a9a8fa16e7a.log
/usr/sbin/mysqld, Version: 8.0.16 (MySQL Community Server - GPL). started with:
Tcp port: 3306  Unix socket: /var/run/mysqld/mysqld.sock
Time                 Id Command    Argument
2019-05-25T08:17:19.278525Z    26 Query show variables like 'general_log%'
2019-05-25T08:18:11.802232Z    25 Query select 1

投げたクエリはちゃんとログファイルに書かれていた🎉

補足 (1)

本番環境でクエリログを取る場合、下記に注意すること

  • ログのぶんだけディスクを食うし、ディスクI/Oが増えるぶんパフォーマンスも劣化する
  • 機微情報を扱うクエリが投げられる場合、ログにも機微情報が乗るのでログファイルは慎重に扱う

補足 (2)

デフォルトではgeneral logはファイルに吐き出されるが、 log_output という変数を変更することで吐き出し先をテーブルに変更することもできる

mysql> set global log_output = "table";

log_output

"[skip ci]" とコミットメッセージに書くと、CIをスキップできる

TravisCIやCircleCIを使ってCI/CDするのはだいぶメジャーになりましたね。

ドキュメントのみの更新時やwipのときなど、「git commitはしておきたいけどCIは走らせなくて良いだよな〜」というときありますよね。

そういうとき [skip ci] とコミットメッセージに含めておくとCIをskip出来て便利です。他にもいくつかバリエーションが有るので、手に馴染むものを使えば良さそう。

参考資料

TravisCI

docs.travis-ci.com

If you don’t want to run a build for a particular commit for any reason, you may instruct Travis CI to skip building this commit via a command in the commit message.

The command should be one of the following forms:

[ skip]

or

[skip ]

where is either ci, travis, travis ci, travis-ci, or travisci. For example,

[skip travis] Update README

CircleCI

circleci.com

By default, CircleCI automatically builds a project whenever you push changes to a version control system (VCS). You can override this behavior by adding a [ci skip] or [skip ci] tag anywhere in a commit’s title or description.

ゆるゆる無限LTというクレイジーイベントへ参加してLT3本やってきた

自分もワイワイしている若者ものづくりコミュニティ Oysters主催、ゆるゆる無限LTというクレイジーイベントへ参加した。

oysters.connpass.com

会場はウィルゲートさん提供。サンクス!

f:id:pinkumohikan:20190513195525j:plain
雰囲気

無限LTとwはwww

他の人の LT を聞きながら、LT の資料を作る。 それを繰り返すことで、1日にたくさん LT の経験をしようという会です。

一言で言うと、LT中毒者の集いですね(雑)

話したこと

自己紹介てきなやつ

  • 我はpinkumohikanだぞ!崇めよ
  • サバゲと、ボウリングと、仮想通貨自動売買はいいぞ!

たくさんメール送るのは大変だぞ

  • まだまだ伝達手段としてメールは無視出来ないぞ
    • 長文送りたいとかエビデンスの観点ではまだ代わりは無い
  • 何も考えずにたくさんメール送ったら、全然届かないぞ (スパムフィルタ)
    • 送信者認証、IP/ドメインレピュテーション、etc...
  • おとなしくSendGridとかSESとかのSaaS使うのがよし
    • SDK使えばOP25Bとかの制約も受けないし!
  • Google Postmaster Toolが便利
    • 迷惑メール通報率や、IP/ドメインレピュテーションの確認ができる

ちゃんとサードパーティ製パッケージのバージョンアップやるんだぞ

f:id:pinkumohikan:20190513195026j:plain
オーディエンスを煽る我

スライドはテンション上がったら上げます(あげないやつ)

もっと様子が気になるぞ?

さとだいさんが写真取ってくれたので見るべし

あとは、タグを追うべし

twitter.com

感想

真面目なネタから、技術ネタ、大根おろしの辛さの研究みたいなぶっ飛んでるネタまで聞けてたのしかった(小学生並みの感想)

MySQL8をバージョンアップしたら起動しなくなった

背景

  • 個人プロダクト用にMySQL8を運用していて定期的にバージョンアップを行っている
  • 8.0.14 から 8.0.16 にマイナーバージョンアップしたところMySQLが起動しなくなった 💀

f:id:pinkumohikan:20190502141209p:plain

調査と対応

おもむろにstartしてみる

$ sudo systemctl start mysql
Job for mysql.service failed because the control process exited with error code.
See "systemctl status mysql.service" and "journalctl -xe" for details.

ですよね〜〜〜

素直にprocessのログを見る

$ sudo journalctl -xe
...
Apr 26 00:39:17 $hostname sudo[5013]: $username : TTY=pts/0 ; PWD=/home/$username ; USER=root ; COMMAND=/bin/systemctl start mysql
Apr 26 00:39:17 $hostname sudo[5013]: pam_unix(sudo:session): session opened for user root by $username(uid=0)
Apr 26 00:39:17 $hostname systemd[1]: Starting MySQL Community Server...
-- Subject: Unit mysql.service has begun start-up
-- Defined-By: systemd
-- Support: http://www.ubuntu.com/support
--
-- Unit mysql.service has begun starting up.
Apr 26 00:39:17 $hostname audit[5067]: AVC apparmor="STATUS" operation="profile_replace" info="same as current profile, skipping" profile="unconfined" name="/usr/sbin/mysqld" pid=5067 comm="apparmor_parser"
Apr 26 00:39:17 $hostname kernel: audit: type=1400 audit(1556206757.959:27): apparmor="STATUS" operation="profile_replace" info="same as current profile, skipping" profile="unconfined" name="/usr/sbin/mysqld" pid=5067 comm="a
Apr 26 00:39:19 $hostname trace-agent[945]: 2019-04-26 00:39:19 JST | TRACE | INFO | (pkg/trace/agent/service.go:63 in Run) | total number of tracked services: 0
Apr 26 00:39:23 $hostname systemd[1]: mysql.service: Main process exited, code=exited, status=1/FAILURE
Apr 26 00:39:23 $hostname systemd[1]: mysql.service: Failed with result 'exit-code'.
Apr 26 00:39:23 $hostname sudo[5013]: pam_unix(sudo:session): session closed for user root
Apr 26 00:39:23 $hostname systemd[1]: Failed to start MySQL Community Server.
-- Subject: Unit mysql.service has failed
-- Defined-By: systemd
-- Support: http://www.ubuntu.com/support
--
-- Unit mysql.service has failed.
--
-- The result is RESULT.
...

うーん、何も分からない(キレ気味)

MySQLのログを見る

$ sudo less /var/log/mysql/error.log
...
2019-04-25T15:41:12.267479Z 0 [System] [MY-010116] [Server] /usr/sbin/mysqld (mysqld 8.0.16) starting as process 6246
2019-04-25T15:41:13.263343Z 4 [System] [MY-013381] [Server] Server upgrade from '80015' to '80016' started.
2019-04-25T15:41:15.448593Z 4 [ERROR] [MY-013384] [Server] Could not create server upgrade info file at '/var/lib/mysql/'.
2019-04-25T15:41:15.454470Z 0 [ERROR] [MY-013380] [Server] Failed to upgrade server.
2019-04-25T15:41:15.455397Z 0 [ERROR] [MY-010119] [Server] Aborting
2019-04-25T15:41:17.137374Z 0 [System] [MY-010910] [Server] /usr/sbin/mysqld: Shutdown complete (mysqld 8.0.16)  MySQL Community Server - GPL.

Could not create server upgrade info file at '/var/lib/mysql/'.

このあたりが怪しそう

おもむろにlsしてみる

$ ls -alh /var/lib/mysql/
...
drwxr-x---  2 mysql mysql 4.0K Apr 26 00:41  mysql
-rw-r-----  1 mysql mysql  56M Apr 26 00:41  mysql.ibd
-rw-r--r--  1 root  root     6 Apr 13 00:08  mysql_upgrade_info
drwxr-x---  2 mysql mysql 4.0K Apr 26 00:28  performance_schema
-rw-------  1 mysql mysql 1.7K Sep 30  2018  private_key.pem
...

一個だけroot所有なファイルが有るぞ...!?

mysql_upgrade_infoは本来rootじゃなくてmysql所有であるべきものっぽいので、所有者をmysqlに変えたら直るんじゃね?

$ sudo chown mysql:mysql mysql_upgrade_info
$ sudo systemctl start mysql
$ sudo systemctl status mysql
● mysql.service - MySQL Community Server
   Loaded: loaded (/lib/systemd/system/mysql.service; enabled; vendor preset: enabled)
   Active: active (running) since Fri 2019-04-26 00:51:50 JST; 3s ago
     Docs: man:mysqld(8)
           http://dev.mysql.com/doc/refman/en/using-systemd.html
  Process: 11526 ExecStartPre=/usr/share/mysql-8.0/mysql-systemd-start pre (code=exited, status=0/SUCCESS)
 Main PID: 11578 (mysqld)
   Status: "SERVER_OPERATING"
    Tasks: 38 (limit: 1110)
   CGroup: /system.slice/mysql.service
           └─11578 /usr/sbin/mysqld

おかえり

MySQLのログを見ても、バージョンアップ成功してそう

$ sudo less /var/log/mysql/error.log
...
2019-04-25T15:51:39.763174Z 4 [System] [MY-013381] [Server] Server upgrade from '80015' to '80016' started.
2019-04-25T15:51:42.308523Z 4 [System] [MY-013381] [Server] Server upgrade from '80015' to '80016' completed.
...

なぜ mysql_upgrade_info がroot所有になっちゃったのかは謎だけど、原因が2週間前の操作によるものである可能性が高くてもう追えないし、ログを順に辿っていけば解決出来る問題だったのでひとまずこれにて。

Scalaで使うテンプレートエンジンの検討 (Scalate、Twirl、Beard)

Scalaでテンプレートエンジン使いたくなったので調べた。

www.scala-lang.org

探し方

GitHubでホスティングされているホットなものを探す。

github.com

こういうのを探すときは、

  • そこそこstarがついている (= みんなに使われている)
  • 定期的に新バージョンがリリースされている
  • contributorが一人じゃない

辺りを確認しておくのがオススメ。 そうしておけば、突然ライブラリが消滅したり、時代に取り残されて渋々他のライブラリへの移行を余儀なくされる、みたいな悲しいことを回避出来る確率が上がる。

見つけたもの

探し方 で触れた観点を持って良さげだなと思ったは次の3つ。

1. Scalate

github.com

Mustache、Jade、Scaml、SSPと複数の表記方法をサポートしている。 テンプレートエンジンって「こういう風にかけ!」と表記方法をビシッと指定されているものしか使ったことがなかったが、複数の表記方法から手に馴染むものを選べるのは嬉しいんじゃないだろうか。

とは言えいくつかはHTML特化の表記方法なので「メール本文とかPush通知の文面とかもテンプレート管理したいんじゃー」という場合は、選択肢としてはMustacheかSSP表記のいずれかになる。オススメはSSPで、テンプレート変数が未定義だった場合にはランタイムで怒ってくれる。Mustacheはしれっとブランクになってしまうのでそれに気づかず事故りやすそう。

2. Twirl

github.com

有名な モリモリ フルスタック系フレームワークplayのテンプレートエンジン。 SBT Pluginとして追加する感じで、templateをコンパイルしてくれるのでテンプレート変数の過不足が無くなるのが良い。 テンプレート変数が未定義だった場合に Scalate * SSP だとランタイムで気付けるが、Twirlだとコンパイル時点で気づける。

機能的は良いんだけど、コンパイルオプションで fatal-warningswarn-unused-import を有効にしていると、 TwirlKeys.templateImports := Nil を設定するハックをしないと unused import で怒られて使えないのが難点。

github.com

3. Beard

github.com

ScalateとTwirlに比べるとstarが少ないが、表記方法はMustacheベースで手に馴染みやすい印象。 Last releaseが2016/4で、2019/4/5現在新し目なIssueも無いのでメンテは活発では無さそうな感はある。

その他

今回はJava用のテンプレートエンジンは探してないけど、そっちも探せば他にも良さげなものはあるかも。

他にオススメあったら教えて下さい。