2017年3月8日水曜日

コンテナの標準出力をログファイルに落としホストマシンで確認する方法

概要

どういうことかというとコンテナ上で動作しているバイナリファイルもしくはコマンドがあるとします
そのコマンドは結果を標準出力に吐くとします
標準出力なのでログファイルには残りません

そんな場合にログファイルに出力させかつホストマシン側でも確認できる方法を紹介します

環境

  • Mac OS X 10.12.3
  • Docker 1.13.1

標準出力するコマンドの作成

適当で OK です
このファイルをコンテナ上で動作させます

  • vim cat echo_loop.sh
#!/bin/sh

while :
do
  echo `date`
  sleep 1
done

Dockerfile の作成

コンテナとして動作させるためイメージを作成します
イメージには上記のコマンドがインストールされるようにします

  • Dockerfile
FROM alpine:latest

COPY ./echo_loop.sh /bin
RUN chmod 755 /bin/echo_loop.sh

CMD ["/bin/sh", "-c", "/bin/echo_loop.sh >> /var/log/echo_loop.log 2>&1"]

イメージの作成とコンテナの起動

ビルドしてできたイメージからとりあえずコンテナを起動します

  • docker build -t myalpine .
  • docker run -d myalpine

でコンテナが起動したら exec して /var/log/echo_loop.log に標準出力がリダイレクトされているか確認します

ホストマシンでも見えるようにする

コンテナを起動する時にマウントオプション -v を指定します

  • mkdir log
  • cd log
  • docker run -d -v $(pwd):/var/log/ myalpine

これでホストマシン側にも echo_loop.log が出力されるようになります

おまけ: ローテーションの挙動を確認する

Mac なので newsyslog を使います

  • cd etc/newsyslog.d
  • sudo vim echo_loop.conf
# logfilename                        [owner:group]     mode count size when flags [/pid_file] [sig_num]
/Users/hawksnowlog/log/echo_loop.log hawksnowlog:staff 644  5     10   *    J

一応設定ファイルの説明をすると

  • owner:group・・・hawksnowlog:staff
  • mode 「644」・・・ローテーション後のファイルの権限を 644 にする
  • count 「5」・・・ 5 世代分残す
  • size 「10」・・・10KB 以上だったらローテーションする、「*」の場合はサイズを使わない
  • when 「*」・・・時間でローテーションしない、「$D0」の場合は毎日 0 時にローテーション
  • flag 「J」・・・bz 圧縮する

ポイントってわけではないですが、他のローテーションツールを使う場合でも権限回りは気をつけてください
ローテーション後に root 権限のファイルになるとコンテナから書き込めなくなります
作成したらローテーションしてみます

  • sudo newsyslog -f /etc/newsyslog.d/echo_loop.conf

ログを tail などで見ていると Mar 2 13:07:12 host newsyslog[6655]: logfile turned over due to size>1K というログがでればローテーション成功です
echo_loop.log.0.bz2 という名前のファイルが新しく出来ていると思います
が、ローテーションした echo_loop.log にはログが出力されません

おそらくコンテナ側に kill シグナルを送信しないといけないのが原因だと思います
logrotate の copytruncate なら問題なく出力されると思います (すいません、試せていません)
https://github.com/docker/docker/issues/7333

当然ですが、コンテナを再起動すればログは問題なく出力を再開します

最後に

コンテナ内で動作しているプロセスの標準出力をホスト側で確認する方法を紹介しました
ローテーション回りがまだ解決していませんが logrotate を使えばいけると思います
わざわざマウントしなくても docker にある log drivers を使えばもっと簡単に管理できると思います
log drivers は別途検証したいなと思っています

参考サイト

15 件のコメント:

  1. 初コメです。
    「イメージの作成とコンテナの起動」の欄にて、
    「コンテナが起動したら exec して」と記載がありますが、実際のexecコマンドの例を教えて頂けませんか?
    ド素人なもので、すみません。

    返信削除
  2. docker exec ef26882ca1bb cat /var/log/echo_loop.log
    こんな感じでログがコンテナ内のファイルに出力されているか確認してください
    ef26882ca1bb はコンテナID です

    返信削除
  3. 追加で質問です。
    こちらのDockerファイルを参考にubuntuを呼び出し、"ls"の結果をリダイレクトでファイルに出力しようとしたのですが、
    まず、Dockerイメージをrunする際に"-d"オプションを付けていても瞬時にexitしてしまいますが、永続的に実行させるにはどうしたら
    よいでしょうか?また、runの際にループで保持する方法も試したのですが、execの際に"bash"だけだとコマンド待ちになってしまい、
    終了できなくなります。何か良い方法がありましたらご教示頂けると幸いです。宜しくお願い致します。

    返信削除
  4. ls を無限ループさせてコンテナが起動し続けるようにしてみてください
    例えばこんな感じのスクリプトが動作するようにしてください
    while true; do ls; sleep 1; done

    返信削除
    返信
    1. わざわざ返信ありがとうございます。ご教示頂いた方法をやってみましたが、ファイルではなくコンソールへlsの結果が出力されて、リダイレクトが行われませんでした。Dockerfileの内容ですが、

      FROM ubuntu

      COPY ./list.sh /bin
      RUN chmod 755 /bin/list.sh

      CMD ["/bin/sh", "-c", "/bin/list.sh >> /tmp/ls.txt 2>&1"]

      何か問題が、ありますでしょうか?大変お手数ですが、宜しくお願い致します。

      削除
  5. Dockerfile は貼っていただいたものです

    vim list.sh
    while true; do ls; sleep 1; done
    docker build -t myubuntu .
    docker run -d --name test myubuntu
    docker exec test cat /tmp/ls.txt

    でどうでしょうか、こちらが試した感じだと docker logs には特に何も表示されませんでした

    返信削除
    返信
    1. わざわざ試験して頂きましてありがとうございます。こちらで実行してみましたが、"/tmp"フォルダに"ls.txt"のファイルが生成されず、コンソールにlsの結果が表示されるだけでした。私の希望は、lsの結果のリダイレクト(ファイル出力)なのです。一体、どこが問題なのでしょうか?ちなみに環境ですが、OS→ubuntu 18.04 docker→ubuntu18.04用dockerイメージ 試験機→Raspberry Pi 3B+ です。何卒宜しくお願い致します。

      削除
  6. とりあえず exec でコンテナ上にファイルが作成できていることが確認できたら最後はホスト側でマウントしてから確認してください
    docker run -d -v $(pwd):/var/log/ myalpine
    の部分です

    返信削除
  7. このコメントは投稿者によって削除されました。

    返信削除
  8. 迅速な返信ありがとうございます。
    docker rm testのあと、docker run -d -v $(pwd):/tmp/ --name test myubuntuで起動し、docker exec test cat /tmp/ls.txtを実行したところ、lsの結果がファイル出力されていました。本当にありがとうございました!

    返信削除
    返信
    1. なんと、2回目の実行ではファイルが作成されなくなりました!docker stop testのあと、docker rm testで削除し、再度、docker run -d -v $(pwd):/tmp/ --name test myubuntuで起動し、docker exec test cat /tmp/ls.txtを実行したところ、/tmpフォルダにls.txtができません。2度目の実行では、何か変わるのでしょうか?

      削除
  9. 何度も投稿申し訳ありません。ファイルは作成されていましたが、作成される場所が/tmpフォルダではなく、実行したフォルダに作成されるようですす。また、lsをループしているため、ファイルの内容がlsを複数回実行したのと同じになってしまいます。lsの結果ですので、出力は1回分で良いのですが、多重に出てしまうのは仕方ないのでしょうか?宜しくお願い致します。

    返信削除
  10. $(pwd) までマウントしているのでカレントにファイルが見えます
    cd /tmp
    してから docker コマンドを実行するか
    docker run -d -v /tmp:/tmp/ --name test myubuntu
    でどうでしょうか

    返信削除
  11. 返信ありがとうございます。確かに、カレントのフォルダに生成されるので、/tmpで実行すれば良い訳ですね。ただ、もう1つの課題である「1回だけ実行」は、lsをループしている限り、不可能でしょうか?私自身では、頭の中が無限ループしそうです。宜しくお願い致します。

    返信削除
  12. 自己解決しました!ループ処理を、

    var=0
    while :
    do
    if [ $var = 0 ]; then
    ls
    var=1
    fi
    sleep 1
    done

    として、lsを1回だけ実行するようにしたところ、リダイレクトされたファイルには1回だけの結果が出力されました。
    (自分でも努力せにゃいけませんね)
    本当にありがとうございました!

    返信削除