Docker Linux

systemdからdockerコンテナを起動+timerで定期実行

投稿日:

今回はdockerコンテナをsystemdから起動し、かつtimerを使用して定期実行する方法を紹介します。

背景

VPS上で動作し、定期的に自分にメールを送信してくれるようなアプリケーションを作ろうとしています。
例えば、先に紹介した「Pythonによる株価取得」を行い、1日毎に自身にメールで株価を通知するようなものを考えています。

上記のようなアプリを作成するにあたり、行わなければいけないことは以下のようになります。

  1. アプリケーションを用意する
  2. アプリケーションを定期的に実行する環境を用意する
  3. アプリケーションの実行で得られた結果をメールで通知する環境を用意する

1.については各自で用意するとして、今回は2.について取り上げます。

要求と仕様

要求は以下になります

  • アプリケーションを定期的に実行する
  • 実行時のログを閲覧可能な状態にする
  • dockerコンテナを使用してホスト環境を汚さない

仕様は以下になります

  • systemdのtimer機能で定期実行する
  • journaldの機能でログを閲覧する
  • systemdからdockerコンテナを起動する

systemdについて

systemdは、汎用的にアプリケーション実行を制御するためのデーモンです。
init.dに代わり、最近のLinuxディストリビューションに標準搭載されています。たとえば、Ubuntu16.04(筆者のVPS環境)でも採用されました。

systemdの機能は多岐にわたりますが、代表的なものは、アプリケーションの起動順序制御やログ取得機能、定期実行機能などです。
今回は、ログ取得機能や定期実行機能を使用するために、systemdを使います。

設計・実装

今回は以下のようなアプリ・環境を実装します。

  • pythonで”Hello, World!”を標準出力するテストアプリを作成する
  • pythonの公式doekcerイメージからコンテナを作成し、上記アプリを実行するようにする
  • dockerコンテナ作成からアプリ実行までの動作をsystemdの.serviceファイルで作成する
  • serviceを定期実行するための.timerファイルを作成する

テストアプリとserviceファイルの作成

テストアプリは以下のように適当に作成し、適当なディレクトリに格納します。今回は、/srv/docker/python に格納しておきます。ファイル名はhelloworld.pyとします。

print("Hello, World!")

これを実行するためのserviceファイルを、/etc/systemd/systemに作成します。ファイル名はdocker_helloworld_py.serviceとします。

[Unit]
Description=Hello, World By Python
After=docker.service
Requires=docker.service

[Service]
Type=simple
TimeoutStartSec=0
ExecStartPre=-/usr/bin/docker kill python_hello
ExecStartPre=-/usr/bin/docker rm python_hello
ExecStart=/usr/bin/docker run --name python_hello -v /srv/docker/python:/tmp/host_share python /bin/bash -c "python /tmp/host_share/helloworld.py"
ExecStop=/usr/bin/docker stop python_hello

[Install]
WantedBy=multi-user.target

  • ExecStartPreで、前回コンテナの削除を行います。=-により、失敗しても動作を継続します
  • ExecStartにより、dockerコンテナを起動します
    • -vオプションにて、先ほど作成したアプリがあるディレクトリを指定します。
    • /bin/bashからpythonコマンドを使用し、先程のアプリを起動します
  • ExecStopにより、docker runが終了したらコンテナを削除します

上記serviceファイルをsystemdに読み込ませて実行してみましょう

user@hostname:~$ sudo systemctl daemon-reload # デーモンのリロードを行い、serviceファイルを読める状態にする
user@hostname:~$ sudo systemctl start docker_hellowolrd_py.service # サービスの実行 
user@hostname:~$ sudo systemctl status docker_hellowolrd_py.service # サービスの状態を表示
● docker_hellowolrd_py.service - Hello, World By Python
   Loaded: loaded (/etc/systemd/system/docker_hellowolrd_py.service; disabled; vendor preset: enabled)
   Active: inactive (dead)

Dec 17 01:41:51 hostname docker[16516]: python_hello
Dec 17 01:41:51 hostname systemd[1]: docker_hellowolrd_py.service: Unit entered failed state.
Dec 17 01:41:51 hostname systemd[1]: docker_hellowolrd_py.service: Failed with result 'exit-code'.
Dec 17 01:47:53 hostname systemd[1]: Stopped Hello, World By Python.
Dec 17 01:49:43 hostname systemd[1]: Starting Hello, World By Python...
Dec 17 01:49:44 hostname docker[17533]: Error response from daemon: Cannot kill container: python_hello: No such container: python_hel
Dec 17 01:49:44 hostname docker[17541]: Error response from daemon: No such container: python_hello
Dec 17 01:49:44 hostname systemd[1]: Started Hello, World By Python.
Dec 17 01:49:45 hostname docker[17550]: Hello, World! #アプリの実行結果の標準出力
Dec 17 01:49:45 hostname docker[17678]: python_hello

dockerコンテナからのアプリの実行結果が確認できました。

timerによる定期実行

次に、systemdのtimerを使用して、serviceを定期実行してみましょう
/etc/systemd/system以下に、docker_helloworld_py.timerファイルを作成します。

[Unit]
Description=Hello, World every 1min

[Timer]
OnBootSec=1min
OnUnitActiveSec=1min
# Unit=docker_helloworld_py.service

[Install]
WantedBy=timers.target
  • [Timer]以下で定期実行の動作を記述します
    • OnBootSecでブート時から1min経過してから実行するようにします
    • OnUnitActiveSecで、前回起動時から相対時間で1min経過したら実行するようにします
    • Unitは、定期実行させたいserviceファイルを指定します
      • ~.timerと~.serviceの〜部が一致している場合は、Unitの指定は不要です

作成できたら、timerを実行してみましょう。

user@hostname:~$ sudo systemctl daemon-reload # デーモンのリロードを行い、serviceファイルを読める状態にする
user@hostname:~$ sudo systemctl start docker_hellowolrd_py.timer # タイマーの実行

実際に実行されているか、journaldを使用してログを確認してみます。

user@hostname:~$ sudo journalctl -u docker_hellowolrd_py.service

(中略)

Dec 17 03:20:40 hostname systemd[1]: Starting Hello, World By Python...
Dec 17 03:20:40 hostname docker[1463]: Error response from daemon: Cannot kill container: python_hello: Container fc6aecedc3f3a76e9c62
Dec 17 03:20:40 hostname docker[1473]: python_hello
Dec 17 03:20:40 hostname systemd[1]: Started Hello, World By Python.
Dec 17 03:20:41 hostname docker[1481]: Hello, World! #アプリ実行
Dec 17 03:20:42 hostname docker[1611]: python_hello
Dec 17 03:21:40 hostname systemd[1]: Starting Hello, World By Python...
Dec 17 03:21:40 hostname docker[1737]: Error response from daemon: Cannot kill container: python_hello: Container 4712ff89745a22becf18
Dec 17 03:21:40 hostname docker[1745]: python_hello
Dec 17 03:21:40 hostname systemd[1]: Started Hello, World By Python.
Dec 17 03:21:41 hostname docker[1754]: Hello, World! #アプリ実行
Dec 17 03:21:42 hostname docker[1879]: python_hello

アプリが1分感覚で実行されていることが確認できました。

終わりに

systemdは、従来のinit.dやcronの機能を取り込んだだけでなく、さまざまな要素を一括で制御する非常に汎用的なシステムです。
今回紹介したアプリ起動や定期実行だけでなく、起動順序制御や、さらにはsocketを利用した簡易サーバ機能なども備えます。それらの紹介は、機会があれば行いたいと思います。

-Docker, Linux
-,

執筆者:


comment

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)

関連記事

Ubuntu 16.04 LTS でのDocker実行環境(docker-engine + docker-compose)の構築

今回は、Ubuntu 16.04 LTS でのDocker実行環境の構築を行います。 目次1 背景2 注意3 docker-engineのインストール4 docker-composeのインストール5 …

ハニーポットcowrieをDockerから起動する

設定等でハマったのでメモとして残します。 [2018/5/2 追記] 下記に取り上げたcowrie公式dockerイメージの公開が停止しています。 代わりに、docker上でKippoを用いて低対話型 …

pthread_cancelすると何が行われるのか

仕事でのメモ。 目次1 pthread_cancel2 pthread_cancelで何が行われるのか2.1 シグナル送出2.2 注意2.3 ソースコードを読む2.4 pthread_cancel時の …

Linux+RustでOS自作〜環境構築編〜

目次1 概要2 環境構築2.1 ツール一覧2.2 ツールのインストール3 終わりに4 参考サイト様4.0.1 関連 概要 先日、「30日でできる! OS自作入門」を購入しました。 30日でできる! O …

cmake, googletestを利用したC++開発環境の構築テスト

自学用メモ。 業務で使用しているcmakeの勉強がてら、業務に近いソースツリーを作成した。 googletestを用いた単体テストも合わせて書けるような構成になっている。 テンプレートとして、今後のC …