LoginSignup
3
6

More than 3 years have passed since last update.

時間のかかるプログラムのプロセス終了をSlackで通知してくれるようにする

Last updated at Posted at 2021-01-20

概要

時間のかかるプログラム(ディープなモデルの学習など) のプロセスの終了を Slack で通知してくれるようにしたので、そのメモ。

特徴としては、

  1. プログラム実行環境にて、追跡するプログラムのプロセスIDを引数とするシェルスクリプトを実行
  2. Slack にて、追跡開始の確認、およびプログラムの実行コマンドの表示
  3. Slack にて、プログラムの終了通知、および追跡を終了するプロセスIDの表示

を行います。

使用ツール & 環境

  • 使用ツール

  • 環境

    • Ubuntu 18.04

シェルスクリプトをほぼ初めて触ったので、冗長なコーディングがあればご指摘お願いします。

導入手順

1. Slack のチャンネルに Incoming Webhook を連携させる

Incoming Webhook へ飛んで、自分の属するワークスペースのチャンネルに連携させてください。

ED6BFF1E-9A49-4D47-A1E4-E8EF6CEF7BFC.jpeg

各自お好みで設定していただければと思います。ここで Webhook URL が、この後の作業で必要となります。

2. プロセスの追跡を行うシェルスクリプトの作成

僕は、以下のディレクトリ構成を作成しました。わかりやすければどこでも良いと思います。

~/
└ Documents/
      └ slack/
          └ (ここに作成するよ)

まず、 ~/Documents/slack/ に以下のコマンドでシェルスクリプトを作成します。

$ touch watch # 名前もなんでも良いと思います。

次に、エディタで watch を開きます。そこへ僕は以下のシェルスクリプトを記述しました。

$ vi watch
watch
#!/bin/bash

set -eu

URL="" ### ここにWebhook URLを記述! ###
USERNAME="あおい姐さん"

PID=$1
if [ ! $PID ]; then
  echo "ERROR: PID required." >&2
  exit 1
fi

# 実行開始時のプロセス
START_MSG="`ps ho args $PID`"
ret=$?
if [ $ret -ne 0 ]; then
  echo "ERROR: Process not found." >&2
  exit 1
else
  ary=(`echo $START_MSG`)
  for i in `seq 1 ${#ary[@]}`
  do
  set +u
    if [ "$i" -eq 1 ]
    then
        PRETXT="${ary[$i-1]##*/} を実行したよ"
    else
        TEXT="$TEXT ${ary[$i-1]}"
    fi
  done
fi
set -u

TITLE="実行コマンド"
TEXT="\`\$ ${ary[0]##*/}$TEXT\`"
BEGIN_DATA="payload={\"username\": \"$USERNAME\", \"text\": \"追跡開始! (PID: \`$PID\` )\", \"attachments\": [{\"fallback\": \"実行コマンド確認\",\"color\": \"#003399\",\"pretext\": \"$PRETXT\" ,\"title\": \"$TITLE\",\"text\": \"$TEXT\"}]}"

curl -s -X POST --data-urlencode "$BEGIN_DATA" ${URL} >/dev/null


# プロセスの終了時
END_MSG="${ary[0]##*/} が終了したよ\n追跡終了! (PID: \`$PID\` )"
: "start watch ${PID}"
{
  while true
  do
    if ! ps -p ${PID} >/dev/null ;then
      curl -s -X POST --data-urlencode "payload={\"username\": \"$USERNAME\", \"text\": \"${END_MSG}\"}" ${URL} >/dev/null
      exit
    fi
    sleep 1m
  done
} &

こちらのコードを参考にさせていただきました。本当にありがとうございます。

参考 : https://gist.github.com/ohsuga/41a459792748e8c2afdb81d0b261c3de

3.テスト

ここまでできたら、一度テストしてみましょう。

まず、適当にPythonで30秒カウントするソースコードを作成します。同時にプロセスIDも出力してくれるようにos.getpid()をprintするようにします。

$ vi test.py
test.py
import os
import sys
import time

def print_args(pid, args):
    print(os.getpid(), args)

if __name__ == "__main__":
    args = sys.argv
    print_args(os.getpid(), args)

    for i in range(0, 30, 1):
        time.sleep(1)
        print(i, "sec")

そしてテストのコマンドが以下になります。コマンドライン引数のテストも兼ねて色々付け足しておきましょう。
ここで、プロセスIDはプロセスごとで異なるので、臨機応変に対応お願いします。以下の場合ですと17027です。

$ python test.py  1 2 3 hello --args-conf
17027 ['test.py', '1', '2', '3', 'hello', '--args-conf']
0 sec
1 sec
2 sec
3 sec
...

30秒以内にすかさず以下のコマンドを実行しましょう。

$ bash watch 17027

そうすると Slack の Incoming Webhook を連携したチャンネルに以下のような投稿がされると思います。
Screenshot from 2021-01-20 23-12-45.png
そして30秒が経過すると。。。

26 sec
27 sec
28 sec
29 sec
$ 

タイムラグがあると思いますが、以下のような投稿がされると思います。
Screenshot from 2021-01-20 23-17-16.png

4. PATHを通す

せっかくコマンドを作成したので、PC上のどこからでもアクセスできるようにPATHを通してしまいましょう。

まずは以下のコマンドで~/.bash_profileにシェルスクリプトのPATHを記述します。

$ echo 'export "PATH=$HOME/Documents/slack:$PATH"' >> ~/.bash_profile
$ source ~/.bash_profile

エイリアスも設定して、bash を打たずに実行できるようにしましょう。僕は aoi というコマンドで実行できるようにしました。

$ echo "alias aoi='bash watch'" >> ~/.bash_aliases
$ source  ~/.bash_aliases

これでコマンドラインにaoiと打ってみて、以下のようなエラーが出ていればPATHが通っています。

$ aoi
/home/user/Documents/slack/watch: line 11: $1: unbound variable

実行例

PyTorchのチュートリアルであるMNISTの分類タスクをやってみましょう。こちらはtorchtorchvisionを必要とするのでご注意ください。

コードを持ってきたら、まずは以下のコマンドでmainのコードを走らせましょう。

$ python main.py --no-cuda
Train Epoch: 1 [0/60000 (0%)]   Loss: 2.305401
Train Epoch: 1 [640/60000 (1%)] Loss: 1.359780
Train Epoch: 1 [1280/60000 (2%)]        Loss: 0.830692
Train Epoch: 1 [1920/60000 (3%)]        Loss: 0.620273
Train Epoch: 1 [2560/60000 (4%)]        Loss: 0.354148
Train Epoch: 1 [3200/60000 (5%)]        Loss: 0.461558
Train Epoch: 1 [3840/60000 (6%)]        Loss: 0.278612
...

そしてaoiを実行するためにプロセスIDを確認しましょう。以下のコマンドで確認できると思います。

$ ps x | grep "python main.py"
11313 pts/0    Rl+    0:10 /home/user/.pyenv/versions/3.6.7/bin/python main.py --no-cuda
11387 pts/5    S+     0:00 grep --color=auto python main.py

おそらくプロセスIDは11313ですね。それでは、aoiを実行しましょう!

$ aoi 11313

すると。。。
Screenshot from 2021-01-20 22-22-49.png
こんな感じで学習の開始と終了を通知してくれるようになりました。

おわりに

まだまだ発展途上の段階の実装だと思うので、導入の際は注意してください m(_ _)m

今後の課題としては

  • プロセスIDの確認の手間を省きたい
  • Slack にて、学習のログの表示やエラーによる停止の通知など拡張したい

したいなと思ってます。

もしよろしければ GitHubリポジトリ へのContribution絶賛お待ちしております!

3
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
6