パラボラアンテナと星の日記

あることないこと

AWS Data Pipelineをcron代替のジョブスケジューラとして利用、AWS外のワーカーで動かす

AWS Data Pipelineをスケジューラとして使う - Qiitaクラスメソッドさんのブログなどを読み、前から気になってた AWS Data Pipeline を利用してみました。

無理やりDocker使ってますが、ワーカーを複数立ち上げるためのツールとして使ってるだけなので、Dockerはあまり関係ありません。

1日しか触ってないので、なにか間違いとかあれば、教えて欲しいです(いつものことながらすいません)

使う前の印象

f:id:hoppie:20150823125933p:plain

使った後の印象

「完全にCrondの代替です 本当にありがとうございました」

↑これは言い過ぎかもしれませんが。

先述した記事にもあったとおり、ShellCommandActivity というのがとても強力で、このような印象を持ちました。

以下、自分なりに説明を試みます

例えばこんなことができる

または

  • 1日1回、あるスクリプト
    • 自分の管理するオンプレサーバ(EC2インスタンス、またはさくらとかDigitalOceanとか)で実行する。

という感じのことが出来ます。

前者もかなり魅力的なのですが、今回は後者のシンプルなデモを示したいと思います。

Pipelineの設定

「Pipeline」とは、『一連のタスクをひとかたまりにしたもの』 みたいな理解で大丈夫だと思います。

さきに、デモにあたり、以下のポリシーをユーザにアタッチしておきます。

  • AWSS3FullAccess
  • AWSDataPipelineFullAccess

Pipelineを編集する画面は、謎のGUIで、architectという名前です。architect のなんとなくの使い方はこちら。↓ architect はこんな感じのイメージです。

f:id:hoppie:20150823140459p:plain

Pipelineの設定: Activity

今回設定したサンプルのActivityを見てみます。

Activityとは、Pipelineの中の中核となる処理です。

f:id:hoppie:20150823140526p:plain

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のほうが優先されるらしいです。今回は設定しません。

Pipelineの設定: Parameter

実行するシェルコマンド(#{myShellCmd})を定義しています。今回はワーカーが自分のhostnameを吐く簡単なスクリプトを用意しました。

echo "I am $(hostname)." >  ${OUTPUT1_STAGING_DIR}/output.txt

f:id:hoppie:20150823140623p:plain

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分のポイントが入るのでよろしくお願い申し上げます。

www.vultr.com

また、複数個のワーカーで動くことを簡単に示すため、今回はコンテナをラクに立ち上げるために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"]

ポイントとしては

  • AWS製の、DataPipeline用のタスクランナーが必要なのでダウンロードする
    • Javaも必要なのでインストールする
  • WorkerGroup名は、先ほど設定した名前と同じものを設定する

という感じです。

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に出力された結果を見てみます。

f:id:hoppie:20150823140603p:plain

hostnameが出力されています!成功です!

今回は3つのコンテナのうちの2番めのコンテナ(39cc7e823227)がスクリプトを実行したようです!

以上で今回のデモは終わりです!

まとめとか感想とか

疲れたのでまとめです。

  • AWS DataPipelineは、わりとなんでもできるスケジューラ として利用できる!
    • ShellCommandActivity がなんでもできて強力
  • Activityの設定項目でWorkerGroupを指定すると、自前のワーカー でジョブを動かせる
    • 複数ワーカーのうちのどれかが死んでも、生きているワーカーがスクリプトを実行してくれそう
    • つまり、冗長化が効いた、いい感じのスケジューラを構築できそう。
    • 今までcron で動かしてたジョブの冗長化 ができそう
      • でもそれ rundeckできるよ、という意見も。
    • 今回はVPSを利用しましたが、EC2でも良い
      • EC2ではIAMロールとか使えば、credentials.json とかも不要になる気がする
  • (今回のパターンとは別に)「一時的にEC2インスタンスを立ててスクリプトを実行する」も簡単にできそう。
    • これは rundeck より楽にできそう
    • これは時間なかったので別途。。
    • 夢が広がりんぐ

疑問とか

  • そういえばDockerのエコシステムで、いいかんじにcronを冗長化する方法って、普通はどうやるんだろう?
    • AWSに頼らずとも出来るよなー、fleetは近いと思ってたんだが、どうやるんだろう?
    • このへん詳しい人おしえて欲しい…