Introduction
Docker Desktopが起動してなかったら起動させたいし、リソースセーバーが動いてたら再起動をシェルスクリプトでやりたい!をやったというお話です。
さて、大学の講義で演習に使用しているプログラムがあります。
Docker Desktopが起動している前提で動くものなのですが、学生から「なんか実行できないんですが…」と言われて確認すると…
「Docker、起動してないやんけ…!!!」
となることが多いTA(教育補助の学生)さんです。こんにちは。
そんなのにいちいち対応するのはとっても面倒大変なので、コンテナ起動を仲介してる.command(中身はbashスクリプト)からよしなにDockerくんを起動させてしまおう!というのが今回の趣旨です。
想定する環境
- Mac系のPC
- Docker Desktop導入済み
- AppleScriptが使える(正確にはosascript)
結論
# 現在実行されているアプリケーション名を取得
RUN=`osascript -e 'tell application "System Events" to get name of (processes where background only is false)' | grep -c 'Docker Desktop'`
# Docker Desktopがアプリケーションとして起動してないと認識されている場合
if [ ${RUN} = 0 ]; then
echo "Docker Desktop is not Started."
# Dockerがリソースセーバーモードで起動している場合
DOCER_PROCESS_COUNT=`ps aux | grep docker | grep -v grep | wc -l`
if [ ${DOCER_PROCESS_COUNT} -gt 0 ]; then
echo "Docker kernel is run Resource Saver Mode... "
pkill -KILL -f Docker
pkill -KILL -f docker
echo "Docker application killed."
fi
open -g -a /Applications/Docker.app
# 全てのプロセスの起動を待つ
DOCER_PROCESS_COUNT=`ps aux | grep docker | grep -v grep | wc -l`
until [ ${DOCER_PROCESS_COUNT} -gt 6 ]
do
sleep 1
echo "Preparing to launch Docker Desktop ..."
DOCER_PROCESS_COUNT=`ps aux | grep docker | grep -v grep | wc -l`
done
# UI起動後の読み込み待ち
sleep 5
echo "Docker Desktop is running..."
# Docker Desktopがアプリケーションとして起動していると認識されている場合
else
echo "Docker Desktop is running..."
fi
全体の挙動
- Docker Desktopがアクティブに起動していれば、「Docker Desktop is running...」と出力して終了
- Resource Saver Modeで起動していれば、一旦全部のプロセスをkillして再起動
- 起動していなければ、起動
- 全部のプロセスが起動して、UIのコンテナ情報読み込みが終了するまで待機した後、「Docker Desktop is running...」と出力して終了
全体はこんな感じです。
詳細と解説
実行されているアプリケーション名の取得
RUN=`osascript -e 'tell application "System Events" to get name of (processes where background only is false)' | grep -c 'Docker Desktop'`
起動していたら1、していなかったら0がRUNに格納されます。
どうにかして取れないかな〜〜〜と思っていた時にこちらのstackoverflowを見つけて実行したら取れました。
osascriptとは?
AppleScriptをターミナルで実行できるコマンドです。
今回はその後ろの方にあるシングルクオート内がAppleScriptです。
後ろの方のgrep -c 'Docker Desktop'
は得られた結果をパイプで渡して、一致した個数とってくるようにしているものです。
Resource Saver Modeで起動している時はプロセスをkill
DOCER_PROCESS_COUNT=`ps aux | grep docker | grep -v grep | wc -l`
if [ ${DOCER_PROCESS_COUNT} -gt 0 ]; then
echo "Docker kernel is run Resource Saver Mode... "
pkill -KILL -f Docker
pkill -KILL -f docker
echo "Docker application killed."
fi
Resource Saver Modeとは
↑これに見覚えがある人、この状態がResource Saver Modeです。
It significantly reduces Docker Desktop's CPU and memory utilization on the host by 2 GBs or more, by automatically stopping the Docker Desktop Linux VM when no containers are running for a period of time.
つまり、一定時間アクティブなコンテナがなければ自動的にCPUとメモリの使用を制限してくれるというモード。
このモードの最中は、コンテナの起動、終了とかの操作、特にdocker compose
は何があったの?と思うくらい遅くなります。
killする必要あるの?
実はリソース管理面ではかなりありがたい機能なんですが、講義で使うようなパターンだといくつか落とし穴があります。
- 下手すると1週間起動しっぱなしの学生がいる
→次週の講義までに他のアプリケーションによって設定変更とかあるとDockerくんが修繕要求出してくる - 何かしらのエラーが出ても正常だと思い込んで使おうとする
→明らかに止まってて実行できてないのに「ここのコードがうまく動かなくて」と言われて発覚が遅れる - たまに
/Application/DockerDesktop 2.app
なる複製体がいる
→うまく動かないと入れ直ししようとするんだが、その時に前のものを消さずに.dmgからコピーする信じられない挙動をする - 実行に時間がかかるとそれだけでターミナル自体を落としてしまうことがある
→そのまま待ってればできるはずなのに待ってあげないので「なんか起動しなくて…」と言われる(時間の無駄)
知らないって、怖いですよね…。
これらの事象を起こさないようにするためには、そもそもResource Saver Modeを切ればいいわけです。
環境構築もままならない子がいるのに???意味わからんとこ弄られたら困るが???
ということで、こっちでプロセスをkillします。恐ろしいことにそっちの方が安全だからです。
そんなことない人はこっちの手順の方がいいです。
Docker for Mac/Windowsが妙に遅い時はResource Saverをオフに
DOCER_PROCESS_COUNT
ps aux | grep docker | grep -v grep | wc -l
これは「CPUとか割り当てられて現在実行中のプロセスから(ps aux
)、dockerとプロセスに入ってるものを抽出して(grep docker
)、その中から抽出に使用してるgrepプロセスを抜いて(grep -v grep
)、改行の数を数える(wc -l
)」という処理です。
つまりコマンドとしては「Dockerの現在実行中のプロセスの数を数える」というものになります。
プロセスと前述のアプリケーション起動確認のコマンドの状況だけを見れば、Resource Saver Modeは「アプリケーションとしてDockerは起動してないと認識されてるけど、プロセスはある」状況です。
なのでプロセスの個数が0以上であれば、Resource Saver Modeとなります。
後述のcom.docker.virtualizationプロセスを検知するという手段も考えたんですが、「kernelのプロセスがない」を判定するよりも、プロセスの個数のみを見た方が簡単でミスりにくいです。
プロセスが本当に何も起動していない時はもちろんcom.docker.virtualizationプロセスがなく、「Dockerのプロセスはあるがkernelがない」は直接的で分かりやすいですが、条件式としては複雑になります。
プロセスを強制的にkillするので、無意味に叩くのはちょっと…
bashでのif文については【シェルスクリプトBash】 if 文(test文)のオプションまとめを参考にしました。
プロセスのkill
pkill -KILL -f Docker
pkill -KILL -f docker
pkill
は名前を指定してプロセスをkillするコマンドです。
こちらを参考にしました。
Docker Desktopのもつ基本プロセスは以下のもの。
- com.docker.build
- com.docker.dev-envs -watchdog
- /Applications/Docker.app/Contents/MacOS/com.docker.backend
- /Applications/Docker.app/Contents/MacOS/com.docker.backend run
- /Applications/Docker.app/Contents/MacOS/Docker
- UIの表示に使われてるものっぽい
- メニューバーとウィンドウとか起動してるUIの分ある
- /Applications/Docker.app/Contents/MacOS/com.docker.virtualization
- Docker Desktop Linux VMのカーネルと思われるもの
ps aux | grep docker | grep -v grep
で確認できます。
これらのうち、/Applications/Docker.app/Contents/MacOS/com.docker.virtualization以外がResource Saver Modeの時に存在する可能性のあるプロセスです。
わざわざDockerとdockerでやっているのはプロセス名が異なるから。
先にUIをkillしてから本体のプロセスをkillしています。
アプリケーションの起動
open -g -a /Applications/Docker.app
こちらの記事を参考にしました。
全プロセスの起動を待機する
DOCER_PROCESS_COUNT=`ps aux | grep docker | grep -v grep | wc -l`
until [ ${DOCER_PROCESS_COUNT} -gt 6 ]
do
sleep 1
echo "Preparing to launch Docker Desktop ..."
DOCER_PROCESS_COUNT=`ps aux | grep docker | grep -v grep | wc -l`
done
プロセス数の再計算
DOCER_PROCESS_COUNT=`ps aux | grep docker | grep -v grep | wc -l`
「Resource Saver Modeで起動している時はプロセスをkill」の項目で書いた通り。
プロセス数が6より多くなるまで待機する
再掲になるがDocker Desktopのもつ基本プロセスは以下。
- com.docker.build
- com.docker.dev-envs -watchdog
- /Applications/Docker.app/Contents/MacOS/com.docker.backend
- /Applications/Docker.app/Contents/MacOS/com.docker.backend run
- /Applications/Docker.app/Contents/MacOS/Docker
- UIの表示に使われてるものっぽい
- メニューバーとウィンドウとか起動してるUIの分ある
- /Applications/Docker.app/Contents/MacOS/com.docker.virtualization
- Docker Desktop Linux VMのカーネルと思われるもの
ちゃんと全部が起動できれば上4つとメニューバー、メインとなるウィンドウ、カーネルで7つのプロセスが起動するはずです。
それらが全部起動するまで1秒待機して、プロセス数を更新し続けます。
bashの繰り返しについてはとほほのBash入門を参考にしました
UI起動後の読み込み待ち
sleep 5
Docker Desktopはメインとなるウィンドウが起動した後、ローディングがあってからコンテナ一覧とかが表示されると思います。
読み込み中とか処理中に、こっちから割り込みすると予期せぬ挙動をすることがあるので5秒(適当に指定した)待機するようにしています。
PC本体の処理状況やスペックによっては5秒では短いかもしれない。
全体像(再掲)
ということで、以上のことをやっているのがDockerDesktop.appをいい感じで自動起動させる処理です。
# 現在実行されているアプリケーション名を取得
RUN=`osascript -e 'tell application "System Events" to get name of (processes where background only is false)' | grep -c 'Docker Desktop'`
# Docker Desktopがアプリケーションとして起動してないと認識されている場合
if [ ${RUN} = 0 ]; then
echo "Docker Desktop is not Started."
# Dockerがリソースセーバーモードで起動している場合
DOCER_PROCESS_COUNT=`ps aux | grep docker | grep -v grep | wc -l`
if [ ${DOCER_PROCESS_COUNT} -gt 0 ]; then
echo "Docker kernel is run Resource Saver Mode... "
pkill -KILL -f Docker
pkill -KILL -f docker
echo "Docker application killed."
fi
open -g -a /Applications/Docker.app
# 全てのプロセスの起動を待つ
DOCER_PROCESS_COUNT=`ps aux | grep docker | grep -v grep | wc -l`
until [ ${DOCER_PROCESS_COUNT} -gt 6 ]
do
sleep 1
echo "Preparing to launch Docker Desktop ..."
DOCER_PROCESS_COUNT=`ps aux | grep docker | grep -v grep | wc -l`
done
# UI起動後の読み込み待ち
sleep 5
echo "Docker Desktop is running..."
# Docker Desktopがアプリケーションとして起動していると認識されている場合
else
echo "Docker Desktop is running..."
fi
無駄処理あるかも〜と思いつつ、一旦できていそうなのでこれで。
issueにこれ入れたの誰だ(私だな…)と思いつつの開発でしたができてよかった〜!