読者です 読者をやめる 読者になる 読者になる

Digdag on Dockerで何度コンテナを立て直しても状態を維持する

しました
DigdagはDocker上でタスクに依存性をもたせて処理を実行するために使用し始めました

Digdagを使うことが出来るDocker Imageは今回は自前で用意したものを使用します
※ Digdagのバージョンは0.9.10

https://hub.docker.com/r/hatappi/digdag/
ENTRYPOINTにはdigdagを指定しているので docker run hatappi/digdag init hoge みたいに使えます

例えば下記のようなワークフローを/tmp/workflow.dig にホストマシンに定義したとします

timezone: Asia/Tokyo

+setup:
  echo>: SETUP!

+echo_date:
  sh>: date

+cleanup:
  echo>: CLEANUP!

処理としては SETUP! をechoした後に date で現時刻を表示して最後にCLEANUP!と表示して完了します
docker runにてworkflow.digをコンテナ上にマウントして実行します

$ docker run -w /digdag -v /tmp/workflow.dig:/digdag/workflow.dig hatappi/digdag run workflow.dig
2017-05-11 15:35:10 +0000: Digdag v0.9.10
2017-05-11 15:35:12 +0000 [WARN] (main) io.digdag.cli.Run: Using a new session time 2017-05-12T00:00:00+09:00.
2017-05-11 15:35:12 +0000 [INFO] (main) io.digdag.cli.Run: Using session /digdag/.digdag/status/20170512T000000+0900.
2017-05-11 15:35:12 +0000 [INFO] (main) io.digdag.core.workflow.WorkflowExecutor: Starting a new session project id=1 workflow name=workflow session_time=2017-05-12T00:00:00+09:00
2017-05-11 15:35:14 +0000 [INFO] (0016@[0:default]+workflow+setup) io.digdag.core.agent.OperatorManager: echo>: SETUP!
SETUP!
2017-05-11 15:35:15 +0000 [INFO] (0016@[0:default]+workflow+echo_date) io.digdag.core.agent.OperatorManager: sh>: date
Thu May 11 15:35:15 UTC 2017
2017-05-11 15:35:17 +0000 [INFO] (0016@[0:default]+workflow+cleanup) io.digdag.core.agent.OperatorManager: echo>: CLEANUP!
CLEANUP!
Success. Task state is saved at /digdag/.digdag/status/20170512T000000+0900 directory.

そしてもう一度同じコマンドを実行したとする
digdagはそれぞれタスクが完了したかのセッション情報をもっているので本来は完了したタスクはskipされるはずですが、コンテナをたてなおすとセッション情報も保持していないので最初からの実行になってしまいます

これを解決するためにまずdigdagのセッション情報の保持の仕方を確認します
digdagのセッション情報は*.digファイルがあるディレクトリに.dig/[session_time]ディレクトリがきられそこにタスクごとに状態を保持したymlファイルが生成されておりdigdag runした際に該当時間内のsession_timeの情報があるか見に行っているようです

そのディレクトリをDockerのData Volumeを使って永続化してあげれば良さそうです

今回は任意の場所にセッションを保存したかったためdigdag runのオプションの--saveを使用して任意の場所にセッション情報を保持するようにしました
後はそのディレクトリをホストマシンにマウントしてあげるだけです

$ docker run -w /digdag -v /tmp/workflow.dig:/digdag/workflow.dig -v /tmp/dig:/digdag_session hatappi/digdag run workflow.dig --save /digdag_session

これで何回コンテナをたてなおしてもセッション情報をみてdigdag runできるようになりました

コマンド一発で今いるディレクトリに対してhttpでアクセスしたい

題名通りで時々ローカルにあるファイルを file:// のfileプロトコルではなくてhttpでアクセスしたいってことがありました
こんな時はrubyだと library webrick (Ruby 2.4.0) を使うと実現できます

例えば下記のようなhtmlファイルがあったとして

index.html

<html>
  <body>
    <h1>hoge</h1>
  </body>
</html>

ファイルと同じディレクトリにてhttp_server.rbなんてファイルを作成します

require 'webrick'

WEBrick::HTTPServer.new(DocumentRoot: './', Port: '8080').start

あとはコマンドラインにて

$ ruby http_server.rb

でhttpサーバーが立ち上がったので http://localhost:8080 でアクセスすると先程のhtmlファイルが見られるようになります
とはいえhttpサーバーを立ち上げたい時に毎回 .rb ファイルを作成しては面倒
ここで出て来るのがrubyコマンドの -e オプションですね
この後にスクリプトを指定することでコマンドラインから実行することが出来ます

$ ruby -r webrick -e "WEBrick::HTTPServer.new(DocumentRoot: './', Port: '8080').start"

-r を指定することでスクリプトの実行前にKernel.#requireでrequireしてくれます

これでコマンドラインから一発でhttpサーバーが立ち上げれるようになりました

PackerでECRにpushする

—– 2017 / 05 / 15 ——-

PackerでECRにpushする時はaws ecr get-loginの工程はいらなかった - hatappiのブログ

動作としてこの記事でも問題ないのですが、よりよい方法があったので上記書き直しました

最近は個人でも会社でもPackerでDocker Imageをbuildしている
以前GCRを使っていたが今回はAWSのECR を扱う

hatappi.hateblo.jp

こちらはGCRとは違いすでにPacker側で必要な情報だけ指定すればpushできるようになっている

www.packer.io

作業の流れ

まずはECRにリポジトリを登録する
今回はhoge/fuga とした

f:id:hatappi1225:20170508202500p:plain

次にAWS コマンドラインインターフェイス | AWSからコマンドラインawsコマンドが使えるようにしておく

# 事前にaws configureをしておく
$ aws ecr get-login --region ap-northeast-1 # 日本リージョンのECRを使用する時
docker login -u AWS -p [password] -e none https://11111111.dkr.ecr.ap-northeast-1.amazonaws.com

これでdocker loginコマンドが出力される
userはAWSで固定なのでpasswordをひかえておく

Packer用のjsonファイルは下記のように定義します (必要な部分だけを表示してます)
docker-pushの部分で先程のdocker loginコマンドで表示されたものを使用していくだけです
パスワード情報だけはECR_LOGIN_PASSWORDという環境変数で使用できるように設定しておきます

{
  "variables": {
    "ecr_registry_url": "https://11111111.dkr.ecr.ap-northeast-1.amazonaws.com",
    "ecr_repository": "11111111.dkr.ecr.ap-northeast-1.amazonaws.com/hoge/fuga",
    "ecr_login_password": "{{env `ECR_LOGIN_PASSWORD`}}"
  },
  "builders":[{
    "type": "docker",
    "image": "amazonlinux",
    "commit": true
  }],
  ~~
  "post-processors": [
    [
      {
        "type": "docker-tag",
        "repository": "{{user `ecr_repository`}}",
        "tag": "latest"
      },
      {
        "type": "docker-push",
        "login": true,
        "login_email": "none",
        "login_username": "AWS",
        "login_password": "{{user `ecr_login_password`}}",
        "login_server": "{{user `ecr_registry_url`}}"
      }
    ]
  ]
}

これでbuildすればpushされるはずです

lsofは指定されたポートを使用しているプロセスを調べるためだけではなかった

普段任意のポートを使用しているプロセスはなんだろうってなった時にlsofを使う

$ lsof -i:80 # port:80を使用しているプロセス
COMMAND PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
nginx     1 root    6u  IPv4 229552      0t0  TCP *:http (LISTEN)

おっport:80はnginxだね。のような感じ
このlsofはポートを調べる時にしか使ってなかったのでそういう用途かと思ったら他にもあったので覚えておくためにも記録しておく

まずはどんなオプションがあるんだっけの時のman lsof
descriptionにはApple DarwinLinux, FreeBSD, SolarisなどのUNIXによって開かれたファイルの情報を出力するということが書いてある
とりあえず見てみる

特定のポートを使用しているプロセスを調べる

これは冒頭でのlsofでの使用例でも出したものです

$ lsof -i:80 # port:80を使用しているプロセス
COMMAND PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
nginx     1 root    6u  IPv4 229552      0t0  TCP *:http (LISTEN)

指定されたファイルを開いているプロセスは何か

例えばrailsのログを指定してみる

$ lsof development.log
COMMAND PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
ruby      1 root    8w   REG   0,73    10899 1958 development.log

$ ps 1
PID TTY      STAT   TIME COMMAND
  1 ?        Ssl    0:03 /usr/local/bin/ruby bin/rails server -b 0.0.0.0

このことからdevelopment.logはrails serverが開いていることが分かった

指定されたプロセスが開いているファイル一覧

さきほどは指定されたファイルからプロセスを指定したが次は特定のプロセスが開いているファイル見る

$ lsof -p 1
COMMAND PID USER   FD   TYPE DEVICE SIZE/OFF   NODE NAME
ruby      1 root  cwd    DIR   0,73     4096     13 /usr/src/app
ruby      1 root  rtd    DIR   0,73     4096      2 /
ruby      1 root  txt    REG   0,73   142408     39 /usr/local/bin/ruby
~~
uby      1 root  mem    REG   0,73    14664     46 /lib/x86_64-linux-gnu/libdl-2.19.so
ruby      1 root  mem    REG   0,73   137384     44 /lib/x86_64-linux-gnu/libpthread-2.19.so
ruby      1 root  mem    REG   0,73 14453344     42 /usr/local/lib/libruby.so.2.3.0
ruby      1 root  mem    REG   0,73   140928     33 /lib/x86_64-linux-gnu/ld-2.19.so
ruby      1 root  mem    REG   0,73    26258   1422 /usr/lib/x86_64-linux-gnu/gconv/gconv-modules.cache
ruby      1 root    0r  FIFO   0,10      0t0 255817 pipe
ruby      1 root    1w  FIFO   0,10      0t0 255818 pipe
ruby      1 root    2w  FIFO   0,10      0t0 255819 pipe
ruby      1 root    3r  FIFO   0,10      0t0 255913 pipe
ruby      1 root    4w  FIFO   0,10      0t0 255913 pipe
ruby      1 root    5r  FIFO   0,10      0t0 255914 pipe
ruby      1 root    6w  FIFO   0,10      0t0 255914 pipe
ruby      1 root    7w   CHR    1,3      0t0 255878 /dev/null
ruby      1 root    8w   REG   0,73    10899   1958 /usr/src/app/log/development.log
ruby      1 root    9u  IPv4 255918      0t0    TCP *:3000 (LISTEN)

log以外にも色々出てきましたね

指定された秒数ごとにlsofコマンドを再実行してくれる

5秒ごとに再実行される
$ lsof -i:3000 -r5
COMMAND PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
ruby      1 root    9u  IPv4 255918      0t0  TCP *:3000 (LISTEN)
=======
COMMAND PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
ruby      1 root    9u  IPv4 255918      0t0  TCP *:3000 (LISTEN)
=======