それマグで!

知識はカップより、マグでゆっくり頂きます。 takuya_1stのブログ

習慣に早くから配慮した者は、 おそらく人生の実りも大きい。

cronをWEB管理するソフトを作りました。

cron の代替を作りました。

github.com

f:id:takuya_1st:20210917182720p:plain

経緯

cron(自動実行プログラム)の管理が煩雑でした。

増え続けるcrontab。ここ数年管理が崩壊していました。systemd.timer や google app scripts なども増えるし、ラムダ系で実行してたりいろいろな所で、タイマー(カレンダー)による実行が溢れかえる。手軽に作れる一方でエラーログを確認したりがめんどくさいので放置してて実行してるけど動いてないスクリプトとかもあり全容把握が困難でした。

散らばる cron / systemd.timer

crontab による管理は、3つの箇所に別れます。

システム(/etc/cron.d/) root ( /var/spool/cron/crontabs/root ) そして個人ユーザー ( /var/spool/cron/crontabs/$USER ) の3つに別れます。この時点でいい加減に管理していると大変。

最近は、systemd.timer が増えてきました。 systemd.timer は systemd.service とペアになっているので、ファイルが複数に分かれます。さらに、ユーザー空間の systemd ( systemd --user )もあります。ログ管理や任意実行は平易化したが、ファイル管理は煩雑になりました。

ここに、lxd/docker/vbox などの仮想環境のcronが加わると管理は不可能になります。

散らばる通知・ログ

大変なのはログと通知の管理です。スケジューラーがコケたときに通知くるけどcron などは > /dev/null とか > /var/log/my-job.log みたいにログファイルに書くしかなくて困る。エラー時の実行ログと正常ログを見たいのに、実行ログと実行時間から推察してログをたどるのが面倒だった。

crontab 書くのめんどくさい

ちょっとした分岐が書きたい

crontab で実行しようとすると、スクリプトいったん実行可能なファイルにして書き出す必要があって、条件分岐をかくのが大変だし、実行ファイルを一箇所に集めて管理とか大変だ。crontab 見ても何が実行されるのかパット見わからないことが多くて困る。

一時停止が大変

ジョブがエラーになってるときなど、一時停止したいときコメントアウトで対応してるとコメントアウトだらけになって、何がなんだかわからなくなった。

crontab の実行予定がわからない。

大量にあるとスクロールしながらひと目見て、cron 書式から次回実行はいつなんだというのがわかりにくい。ソートしたい。

ssh経由でリモート実行したい

cron を一箇所に集めて、sshでリモート実行ちゃえばある程度解決する。SSHで実行するとなると環境変数ssh-agentを書いて環境変数をうまく扱う必要がある。するとスクリプトファイルを使って実行するがcrontabファイルで完結しなくなる。

cron 限界じゃね?

というわけで、cronは5~10件が限界じゃね?別の何かが必要じゃないかと思いました。

webで管理したい。

webでcronの実行予定とスクリプト本文を編集できたらいいじゃん。

web管理機能を探したけど

いいものが見つからなかった。ジョブスケジューラは高機能すぎる気がするしjenkinsとかはCI/CDツールだし。どうも用途に合わないので、laravelでcron書式をつかったスケジュール実行ができるのでそれを応用して作りました。

ソフトウェア仕組み

root で スケジュール実行用のワーカーを常駐させて、毎分単位で実行するべき処理を探しては実行する。

このソフトウェアの限界

毎分単位で処理を探して、プロセスをforkしてジョブを実行するので、1000件くらいが上限だと思われる。

このソフトの特長

WEBから編集ができます。

一時停止状況がわかるように

コメントアウトの代わりに一時停止状況がわかるように

f:id:takuya_1st:20211004210305p:plain

次回実行の予定がわかるように

cron表記を日本語表記にして次回実行がわかるようにしておいた。

f:id:takuya_1st:20210917182425p:plain

実行ごとに結果がわかるように

実行ごとにSTDOUT/STDERRを保存するように。また実行時間も測定し、スケジュール全体の見直しに活用できるようにしました。

f:id:takuya_1st:20210917182531p:plain

スクリプト本文の直接記述

crontab が1コマンド(1行)で記述しなくちゃいけなくて改行を入れてるとおかしくなるのでとても不便なので、

改行やコメントを書けるようにして、直接記述できるようにしして、実行シェルをbash以外から選べるようにしておいた。現在はbash/phpを選べます。sh/rubyは行けそうなのでそのうち対応しようと思います。pythonはちょっと難しいかも。

f:id:takuya_1st:20211004210436p:plain

インストールと取得はgithub

Github に公開しておきます。

github.com

とりあえず試す用のdockerも用意しました。

docker pull ghcr.io/takuya/cronjob-alternative:latest
docker run --rm -p 5000:80 ghcr.io/takuya/cronjob-alternative:latest

cronといってますが、cron表記を使うだけで、crontabの編集を一切やりません。実行するワーカーはcronとは別にじぶんで起動して使います。

multipass のubuntu で dockerd を動かして外部から接続できるようにする。

multipass に docker を入れる。

multipass を windowsでインストールすると、HyperVの仮想マシンで起動します。

choco install multipass 

windowshyper-V で起動したubuntu に docker を入れる。

sudo snap install docker 

mutipass な ubuntu の docker(dockerd) が外部接続を受けるようにする。

dockerd の起動設定を変える。

# ExecStart=/usr/bin/snap run docker.dockerd  
ExecStart=/usr/bin/snap run docker.dockerd  -H tcp://0.0.0.0:2375 -H unix:///var/run/docker.sock

リロード

sudo vim /etc/systemd/system/snap.docker.dockerd.service
sudo systemctl daemon-reload
sudo systemctl status snap.docker.dockerd.service

外部(Windows/Mac)からmultipass のdocker に接続する。

multipass の仮想マシンtcp://172.17.231.118:2375 でlisten しているので、そこへ接続する。

DOCKER_HOST=tcp://172.17.231.118:2375 /usr/bin/docker run --rm hello-world

docker コマンドをインストールする。

dockerd を除いた、docker コマンドだけをインストールするには

wslv1 のDeibanの場合。

curl -fsSL https://download.docker.com/linux/debian/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
echo   "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/debian \
  $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt update
## 
sudo apt install docker-ce-cli

windows内に複数の仮想環境が乱立するが、TCP経由でdockerd を使うと選択肢が多く、比較的インストールがスムーズに終わることがわかる。

Windows内部の構造を単純に考えると次のようになる。

windows 内部
   - multipass 
       - ubuntu 
   - wsl
     - v1 
       - debian 

docker-desktop でお手軽なんだけど、docker-desktop 版をいれるとwslv2 になるのでめんどくさいんですよ。

別ホストのdockerをTCP経由で操作する

docker は自PCに入れなくても動作します。

docker コマンドの接続は次のようになっています。

docker-cli ---- fd(sock) --- docker-host

docker は api 経由で動作しているので、TCP経由でも接続できます。

docker-cli ---- tcp(sock) --- docker-host

tcp 接続でdocker を使う。

tcp://192.168.2.10:2375 で起動したdockerへ接続する。

DOCKER_HOST=tcp://192.168.2.10:2375 /usr/bin/docker ps -a

docker daemon ホストを tcp://192.168.2.10:2375 で起動する。

TCP経由でリッスンし起動する。

/usr/bin/dockerd -H fd:// -H tcp://0.0.0.0:2375 --containerd=/run/containerd/containerd.sock  

systemd の記述を変える。

/etc/systemd/system/multi-user.target.wants/docker.service

ExecStart=/usr/bin/dockerd -H fd:// -H tcp://0.0.0.0:2375 --containerd=/run/containerd/containerd.sock

リロード再起動

sudo  systemctl daemon-reload
sudo systemctl restart docker.service

活用法

WindowsでDockerを使うときは茨の道なので、TCP経由で起動するといいです。

wsl → localhost:2375  → Windows Docker 
wsl → localhost:2375  → HyperV Docker
wsl → 172.16.11.11:2375  → multipass ubnuto docker

参考資料