AWS Data Pipelineをcron代替のジョブスケジューラとして利用、AWS外のワーカーで動かす
AWS Data Pipelineをスケジューラとして使う - Qiita やクラスメソッドさんのブログなどを読み、前から気になってた AWS Data Pipeline を利用してみました。
無理やりDocker使ってますが、ワーカーを複数立ち上げるためのツールとして使ってるだけなので、Dockerはあまり関係ありません。
1日しか触ってないので、なにか間違いとかあれば、教えて欲しいです(いつものことながらすいません)
使う前の印象
- 「 … Google Cloud Dataflowみたいなやつ?」
- 「でも AWS Data Pipelineをスケジューラとして使う - Qiita とか読むと、『 Crondの代替としてスケジューラとしても使える』のかな?」
使った後の印象
「完全にCrondの代替です 本当にありがとうございました」
↑これは言い過ぎかもしれませんが。
先述した記事にもあったとおり、ShellCommandActivity というのがとても強力で、このような印象を持ちました。
以下、自分なりに説明を試みます
例えばこんなことができる
または
という感じのことが出来ます。
前者もかなり魅力的なのですが、今回は後者のシンプルなデモを示したいと思います。
Pipelineの設定
「Pipeline」とは、『一連のタスクをひとかたまりにしたもの』 みたいな理解で大丈夫だと思います。
さきに、デモにあたり、以下のポリシーをユーザにアタッチしておきます。
- AWSS3FullAccess
- AWSDataPipelineFullAccess
Pipelineを編集する画面は、謎のGUIで、architect
という名前です。architect
のなんとなくの使い方はこちら。↓ architect
はこんな感じのイメージです。
Pipelineの設定: Activity
今回設定したサンプルのActivityを見てみます。
Activityとは、Pipelineの中の中核となる処理です。
Data Pipelineでは、「Redshiftにファイルをロードする」とか「データベースをコピーする」などの便利Activityが、事前にいろいろ定義されているのですが、
今回はなんでもアリの「ShellCommandActivity
」を利用しています。
「ShellCommandActivity
」とは、「与えたシェルコマンドを実行する」というActivityのタイプの1種です。なんでもアリのすごいやつです!!!
Activity内でのその他の項目については、今回は下記のように設定しました。
Output
- アウトプットです。今回はアウトプット先はS3を指定します。
Input
- インプットです。今回は簡単にするためにインプット不要なコマンドにしたため、設定しませんでした。
Stage
- 特に
true
のままいじってませんが、true
にすると、シェルからインプット/アウトプットのディレクトリを変数として参照できるそうです。(${OUTPUT1_STAGING_DIR}
みたいな感じにして参照できる)
- 特に
Command
- 実行するシェルコマンド。
#{myShellCmd}
と書いてありますが、myShellCmd
の中身は別途パラメタとして渡しています。
- 実行するシェルコマンド。
WorkderGroup
- 実行するワーカーのグループ名です。自分で適当に命名します(あとでワーカー側にも設定します)。
RunsOn
- EC2やEMRなどのリソースを起動するときに指定します。
WorkerGroup
と両方設定した場合、RunsOn
のほうが優先されるらしいです。今回は設定しません。
- EC2やEMRなどのリソースを起動するときに指定します。
Pipelineの設定: Parameter
実行するシェルコマンド(#{myShellCmd}
)を定義しています。今回はワーカーが自分のhostnameを吐く簡単なスクリプトを用意しました。
echo "I am $(hostname)." > ${OUTPUT1_STAGING_DIR}/output.txt
Pipelineの設定: その他
他には、Schedule
で実行時刻等を、Precondition
で前提条件等を、設定できるようです。あまりちゃんと見ませんでした。省略。
Pipelineの設定: 今回の全容
今回の全容です。Exportボタンでjsonを出力できます。
{ "objects": [ { "directoryPath": "#{myS3OutputLoc}/#{format(@scheduledStartTime, 'YYYY-MM-dd-HH-mm-ss')}", "name": "S3OutputLocation", "id": "S3OutputLocation", "type": "S3DataNode" }, { "failureAndRerunMode": "CASCADE", "schedule": { "ref": "DefaultSchedule" }, "resourceRole": "DataPipelineDefaultResourceRole", "role": "DataPipelineDefaultRole", "pipelineLogUri": "s3://hoshino-sample-bucket/logs/", "scheduleType": "cron", "name": "Default", "id": "Default" }, { "output": { "ref": "S3OutputLocation" }, "stage": "true", "name": "ShellCommandActivityObj", "id": "ShellCommandActivityObj", "workerGroup": "my_worker_group", "type": "ShellCommandActivity", "command": "#{myShellCmd}" }, { "occurrences": "4", "period": "1 days", "name": "Every 1 day", "id": "DefaultSchedule", "type": "Schedule", "startAt": "FIRST_ACTIVATION_DATE_TIME" } ], "parameters": [ { "description": "S3 output folder", "id": "myS3OutputLoc", "type": "AWS::S3::ObjectKey" }, { "default": "デフォルトの値がここに入る。今回はオーバーライドするのであまり関係ない", "description": "S3 input folder", "id": "myS3InputLoc", "type": "AWS::S3::ObjectKey" }, { "default": "デフォルトのコマンドがここに入る。今回はオーバーライドするのであまり関係ない", "description": "Shell command to run", "id": "myShellCmd", "type": "String" } ], "values": { "myShellCmd": "echo \"I am $(hostname).\" > ${OUTPUT1_STAGING_DIR}/output.txt", "myS3InputLoc": "s3://hoshino-sample-bucket/input-test", "myS3OutputLoc": "s3://hoshino-sample-bucket/dir1" } }
ワーカー側の設定
次に、ワーカー側の設定です。
AWS以外でも実行できることを示すため、(なんでもいいのですが)今回はVultrVPSを使いました。
関係無いですがVultr、以下のリンクから契約していただけると私に$10分のポイントが入るのでよろしくお願い申し上げます。
また、複数個のワーカーで動くことを簡単に示すため、今回はコンテナをラクに立ち上げるためにDockerとCoreOSを使いました。
CoreOSではユーザ core
なので ホームディレクトリ(/home/core
) に AWSの認証情報credentials.json
を置きます。
今回は以下のようなcredentials.json
で動作しました。
{ "access-id": "AKIA****", "private-key": "シークレットキー", "region": "ap-northeast-1" }
次に以下の様なDockerfileを用意します。
Dockerfileで記録を残しておくと、再現性があってべんりです。
コンテナの中のOSは、なんでもいいのですが、今回は慣れてるCentOSにしました。
FROM centos RUN echo "yum update -y; yum install -y java sudo;" | sh # rootで走らせたくないのでユーザ準備する # デバッグ用途でも使うのでパスワード不要でsudoできるようにする(ほんとうはいらない) RUN useradd hoshino RUN echo "hoshino:hoshino" | chpasswd RUN usermod -g wheel hoshino; chmod o+w /etc/sudoers; echo "%wheel ALL=(ALL) ALL" >> /etc/sudoers; echo "%wheel ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers; chmod o-w /etc/sudoers; WORKDIR /home/hoshino USER hoshino # Data Pipeline用のタスクランナー(java実行ファイル)のダウンロード # see http://aws.amazon.com/developertools/AWS-Data-Pipeline/1920924250474601 RUN curl -LO "https://s3.amazonaws.com/datapipeline-us-east-1/us-east-1/software/latest/TaskRunner/TaskRunner-1.0.jar" ENTRYPOINT ["java", "-jar", "TaskRunner-1.0.jar", "--config=/var/volume1/credentials.json", "--region=ap-northeast-1", "--logUri=s3://hoshino-sample-bucket/local_logs"] CMD ["--workerGroup=my_worker_group"]
ポイントとしては
という感じです。
docker build
でイメージを作ります。イメージの名前はもちろんなんでもいいです。
$ docker build -t hoshino/taskrunner .
docker run
します。ワーカーが複数個走っても大丈夫なのを示すため、3つ走らせます。Volume(-v
)でcredential.json
が置いてある場所をコンテナと共有しています。
$ docker run --name test1 -d -v /home/core:/var/volume1 hoshino/taskrunner $ docker run --name test2 -d -v /home/core:/var/volume1 hoshino/taskrunner $ docker run --name test3 -d -v /home/core:/var/volume1 hoshino/taskrunner
$ docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 355018eab46c hoshino/taskrunner:latest "java -jar TaskRunne 3 seconds ago Up 2 seconds test3 39cc7e823227 hoshino/taskrunner:latest "java -jar TaskRunne 8 seconds ago Up 7 seconds test2 231b5f0034c6 hoshino/taskrunner:latest "java -jar TaskRunne 12 seconds ago Up 12 seconds test1
ワーカーにあたるコンテナが、3つ走りました。
結果を見てみる
設定したDataPipelineを Activate
にして、実行してみます。
S3に出力された結果を見てみます。
hostnameが出力されています!成功です!
今回は3つのコンテナのうちの2番めのコンテナ(39cc7e823227)がスクリプトを実行したようです!
以上で今回のデモは終わりです!
まとめとか感想とか
疲れたのでまとめです。
AWS DataPipeline
は、わりとなんでもできるスケジューラ として利用できる!ShellCommandActivity
がなんでもできて強力
Activity
の設定項目でWorkerGroup
を指定すると、自前のワーカー でジョブを動かせる- (今回のパターンとは別に)「一時的にEC2インスタンスを立ててスクリプトを実行する」も簡単にできそう。
- これは
rundeck
より楽にできそう - これは時間なかったので別途。。
- 夢が広がりんぐ
- これは
疑問とか
いまさらだけどcedar-14にアップデートする
古いcedarアプリをpushするとこう言われます
remote: ! Cedar-10 will reach end-of-life on November 4th, 2015. remote: ! Upgrade to Cedar-14 at your earliest convenience. remote: ! For more information, check out the following Dev Center article: remote: ! https://devcenter.heroku.com/articles/cedar-14-migration
herokuの Migrating to the Celadon Cedar-14 Stack | Heroku Dev Center の記事を参考にアップデートします。
$ heroku stack:set cedar-14 --app アプリ名 Stack set. Next release on アプリ名 will use cedar-14. Run `git push heroku master` to create a new release on cedar-14.
これで次のプッシュの時にcedar-14になってくれます!
$ git push -u heroku master Branch master set up to track remote branch master from heroku. Everything up-to-date
コミットハッシュが同じだと何も起きないようです。 空コミットとかつくってプッシュしてみます。
$ git commit --allow-empty -m'blank commit for ceder-14' blank commit for ceder-14 $ git push -u heroku master Counting objects: 1, done. Writing objects: 100% (1/1), 201 bytes | 0 bytes/s, done. Total 1 (delta 0), reused 0 (delta 0) remote: Compressing source files... done. remote: Building source: remote: remote: -----> Ruby app detected remote: -----> Compiling Ruby/Rack remote: -----> Using Ruby version: ruby-2.2.2 remote: -----> Installing dependencies using 1.9.7 ....中略.... remote: -----> Compressing... done, 18.5MB remote: -----> Launching... done, v21 remote: https://アプリ名.herokuapp.com/ deployed to Heroku remote: remote: Verifying deploy.... done. To git@heroku.com:アプリ名.git 6b30982..a90ca0e master -> master Branch master set up to track remote branch master from heroku.
/etc/os-release を見ると14.04になってる
gemの名前を変えたくて、ファイル名やディレクトリ名の一括リネーム
gemの名前を途中で変えたくなった時の話。
gemの名前とか最初にちゃんと考えとけという話ではありますが。
「ファイルの名前変える」と「中身を変える」必要あるんだが、「ファイルの名前を変える」時の話。
./.rspec ./.ruby-version ./.travis.yml ./AAA.gemspec ./Gemfile ./Gemfile.lock ./lib ./lib/AAA ./lib/AAA/version.rb ./lib/AAA.rb ./LICENSE.txt ./Rakefile ./README.md ./spec ./spec/AAA_spec.rb ./spec/spec_helper.rb
AAAの部分をBBBにリネームしたい。が、良いやり方がわからない。。。
そもそも
./lib/AAA_core/AAA_hogehoge.rb
みたいなのがあったときも、AAAの部分はBBBに一括リネームしたいみたいな感じなので、割と複雑なのだ。。
./lib/AAA_core を先にリネームするとAAA_hogehoge.rbのパスが変わってしまう。
ここでの結論
一発でやるのは難しかったんで
$ find . | grep AAA | xargs -I {} echo "mv {} \$(echo {} | sed -e 's/AAA/BBB/g')" mv ./AAA.gemspec $(echo ./AAA.gemspec | sed -e 's/AAA/BBB/g') mv ./lib/AAA $(echo ./lib/AAA | sed -e 's/AAA/BBB/g') mv ./lib/AAA/version.rb $(echo ./lib/AAA/version.rb | sed -e 's/AAA/BBB/g') mv ./lib/AAA.rb $(echo ./lib/AAA.rb | sed -e 's/AAA/BBB/g') mv ./spec/AAA_spec.rb $(echo ./spec/AAA_spec.rb | sed -e 's/AAA/BBB/g')
みたいにしてシェルスクリプトを作ってshに食わせる
$ find . | grep AAA | xargs -I {} echo "mv {} \$(echo {} | sed -e 's/AAA/BBB/g')" | sh
ディレクトリを先にリネームすると失敗するmvがあるけど、何回かやればよい、はず
(良いやり方あったら教えてください。。。)