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

ゆるふわカウンターアタック

Qiitaっぽい時はQiitaで、slideshareっぽい時はslideshareで、preziっぽい時はpreziで、ブログっぽい時はここで

Rundeck はじめました

うちのワンコ初めてカットしました。スッキリです。

f:id:pioho:20160214091218p:plain

cronやJenkinsでがんばってたジョブスケジューリング機能を一元管理する為"Rundeck"を構築しました。

cronはアレなんでやめたいし、Jenkinsは気が付けばあだ名が付くくらいたくさん居るし、執事やとい過ぎだわ

まぁとは言えプラットフォームごとに出来てしまうのは仕方ないとこもあるので、事前にオンプレもAWSも繋がる便利なVPCを作っておきました。
そこにJenkinsでもいいのですが、「おれの仕事CIだし」って聞こえて来そうなのでやめときます。

最近は、AzkabanやらAirflowやらOozieやらジョブスケジューラー界隈が活況ですが、GUIがある程度充実してて必要最低限のスケジュール機能でいいのと冗長化ができるもので、誰でも使えそうって基準でRundeckに決めました。


今の最新は2.6.2がGAとして出ています。

f:id:pioho:20160214091025p:plain

Javaで動くスケジューラーなので手元にあるMacWindowsでも試せます(やったことないけど。。)
Salesforceドリコムやインティメート・マージャーで使ってるようです。

"Rundeckを入れよう"とサブミッションっぽくカジュアルに切ったチケットを見ると1/22に開始している。はや一ヶ月強かかってしまった。
かなり放置して時期もあったが開発側も協力してくれて先週辺りから一気に進んだ。

ググりヒット感で言うと、結構でてきますが、使ってどうこうって話はあまり多くないかなという印象です。
他で書かれてない情報を書ければと思います。

特徴としては

  1. OSSJava/Groovy製でオーケストレーション機能あり

  2. エージェントレス SSH接続できればジョブを実行できます。Ansibleと同じノリ

  3. ジョブネットを作成可能 ジョブAが正常に終わらないとジョブBを実行したくないと言った依存関係を設定できます。

  4. cronと同様の記述が可能 移行が簡単的な感じです。そこまでメリットかはわからないですが

  5. その他 ジョブの再実行、タイムアウト値、ステップ(あとで説明)ごとのエラーハンドラ設定、ジョブ実行や失敗や成功のメールやWebhookでの通知、ジョブの並列度(やりかたはいくつか)設定ができる

※この辺は他の記事にいっぱいあります

Rundeck用語としては

  1. プロジェクト(project)
    ジョブをまとめる単位がプロジェクトです。 プロダクトごとやその本番/ステージング/開発環境ごとに作るのが一般的かなと

  2. ジョブ(job)
    Rundeckがスケジューリング可能な実行単位です。これは特定のプロジェクトに紐付きます。別プロジェクトのジョブを実行することはできません。 ジョブは1つ以上のステップを持ちます。ジョブの実行結果はあとから閲覧できます。

  3. ノード(node)
    ノードはジョブを実行する対象ホストです。 これもプロジェクトに紐付くため、別プロジェクトのノードを利用することはできません。

  4. ステップ(step)
    ステップはジョブを構成する最小単位です。リモート先ノードに配置したシェルやコマンド、ローカルにあるシェル、ローカルにあるシェルを転送してリモート先で実行、シェルが置いてあるURLを指定するとそこからダウンロードしてリモート先で実行とか選べます。
    また作成したジョブをステップとして登録できます。ジョブの中にジョブをネストする感じです。カオスの臭いがしてきますね;
    ただ、これでジョブネットを実現することになると思います。

f:id:pioho:20160225181514p:plain

構成

f:id:pioho:20160225182424p:plain

冗長構成ですが、アクトスタンバイとしました。

rundeck01(アクティブ機) Nginx(Started),Rundeck(Started/Active)
rundeck02(スタンバイ機) Nginx(Stoped),Rundeck(Started/Passive)
  1. スタンバイ側のRundeckはPassiveモード(スケジュールジョブ実行不可)にしておく
  2. アクティブ機が障害が起ったのを検知した際にActiveモード(スケジュールジョブ実行可能)にする
  3. さらにTakeover(実行ジョブの引継ぎ処理)をすることでrundeck02がジョブスケジュールも全て引継ぎアクティブな状態と遷移する

ちなみにAPI使ったActiveモードへ移行はこちら(xxxxxxxxxxはトークン)(アカウントごとにトークン発行可)(disableでpassiveモード)

curl -H 'X-RunDeck-Auth-Token:xxxxxxxxxx' -H "Content-Type: application/xml" -d '' -X POST http://rundeck02:4440/api/14/system/executions/enable

APIを使ったTakeoverはこちら

curl -H 'X-RunDeck-Auth-Token:xxxxxxxxxx' -H "Content-Type: application/xml" -d '<takeoverSchedule><server all="true"/><project all="true"/></takeoverSchedule>' -X PUT http://rundeck02:4440/api/14/scheduler/takeover


プラグインはログをS3に置く"rundeck-s3-log-plugin"と
EC2インスタンスを探してくれる"rundeck-ec2-nodes-plugin"を入れてます
おそらく定番です。

rundeck-plugins · GitHub

その為、ジョブ実行ログはS3にあり、プロジェクト情報はRDSにあり、ノード情報はec2プラグインで動的に取ってくるのでほとんど共有できてるのですが、手動でノード登録が必要なDC側のサーバーだけは共有する必要があります。
こちら

/var/rundeck/projects/プロジェクト名/etc/resources.xml

あと/etc/rundeck/ですね。この辺でACL設定とかします。
プロジェクトをプロダクトごとに分けてマルチテナント的に使う時とかは他の自身のプロジェクトしか見えないように権限付けます(うちがそうです)
なのでバックアップも/etc/rundeck/
一式取ればOKかと
うちはAnsible側に持たせてます

消えないログ

逆に肥大化してくポイントがいくつかあります。問題点ですね。

この辺でがんばってる方いますので参考になると思います
うちもマネてとりあえず手を打ちましたが絶賛模索中です

https://gist.github.com/unicolet/af648a97163ce6b44645
http://www.phwitservices.com/2015/05/clearing-rundeck-log-files/

/var/lib/rundeck/logs/*のジョブ実行ログは消えません。
S3にあるジョブ実行ログも消えません。
RDSにあるジョブ実行関連の情報も消えません。
GUIからBulkdeleteが出来るのですが1度に最大20行しか選べず・・
この辺のテーブル

log_file_storage_request
base_report
execution

リンクを参照し30日を過ぎたジョブ実行IDをfindして
ローカルファイルとDBのレコード削除をしています。
対象テーブルは網羅されてる自信なしです><

#!/bin/sh
cd /var/lib/rundeck/logs/rundeck

JOBS=`find . -maxdepth 3 -path "*/job/*" -type d`

for j in $JOBS ; do
        echo "Processing job $j"
        ids=`find $j -mtime +30 -iname "*.rdlog" | sed -e "s/.*\/\([0-9]*\)\.rdlog/\1/" | sort -n -r`
        declare -a JOBIDS=($ids)

          for job in ${JOBIDS[@]};do
             echo " * Deleting job: $job"
             echo "   rm -rf $j/logs/$job.*"
             rm -rf $j/logs/$job.*
              mysql -h YOURDB -uxxx rundeck -pxxx -e "delete from log_file_storage_request where execution_id=$job"
              mysql -h YOURDB -uxxx rundeck -pxxx -e "delete from base_report where jc_exec_id=$job"
              mysql -h YOURDB -uxxx rundeck -pxxx -e "delete from execution where id=$job"
          done
done

あとはシンプルにAPIから消すことはできます。
が、いついつ以前を削除みたいなことはできず指定JOBIDの実行ログ全て消えます。

curl -H "X-RunDeck-Auth-Token: $RD_OPTION_TOKEN" -H "Content-Type: application/xml" -X DELETE http://rundecksvr:4440/api/12/job/$RD_OPTION_JOBID/executions

$RD_OPTION_TOKEN には権限のあるユーザーのトークン
$RD_OPTION_JOBID には対象のジョブID

 cat /etc/rundeck/apitoken.aclpolicy
description: API project level access control
context:
  project: '.*' # all projects
for:
  resource:
    - allow: '*'
  adhoc:
    - allow: '*'
  job:
    - allow: '*'
  node:
    - allow: '*'
by:
  group: api_token_group

※注意としてはトークン用のACLファイルが別にあるのでそちらにもDELETE権限を付与してあげる必要がありますがとりあえず上記はapi_token_groupに全権限付与してます
※ここ改善されると信じてます。簡単に標準機能であってほしい;;

KeyStorage

これ便利かも
プロジェクトごとに対象の鍵を選択するだけで済むし、RDS側で鍵を持ってくれるので冗長性もあるのでうれしい。

f:id:pioho:20160226121616p:plain

プロジェクト横断

プロジェクト横断したジョブスケジュール設定したいって絶対でますよね。。

プロジェクトを横断したジョブ連動は、今実現するとなると多分・・

  1. ファイルができたことを検知するシェルを1つ目のステップに登録したジョブを作る (ループさせてファイルがあったらexitするよな感じで)
  2. ファイルがあったら別プロジェクトの前段のジョブが終わってるとして、自分のジョブを走らせる

感じかと
めんどくさそうですが汎用性あるジョブにしてしまえば流用可能かと思います。

もっといい方法あれば教えてください><

ジョブが増えてくるとDBサーバーがCPUリークする

CPUリークします。1ヶ月スパンくらいでCPU使用率が右肩上がりで

これはworkflow_workflow_stepというテーブルにインデックスが張られてないことが原因です。workflow_commands_idにインデックス張ります。劇的に改善します。

 ALTER TABLE workflow_workflow_step ADD INDEX workflow_commands_id(workflow_commands_id);

Rundeck との闘争 - 日々是ウケ狙い

外部からキック

外部のサーバーとの連携したい場合はAPI経由でRundeckのジョブキックが可能です。

ジョブID特定
(ローカルからですが・・)

# curl -H 'X-Rundeck-Auth-Token: xxxxxxxxxxxxxxx' "http://rundeck01:4440/api/15/project/test001/jobs"
<jobs count='2'>
  <job id='3a15fbcb-53f3-4cc4-b689-d6f96a9bxxxx' href='http://rundeck01:4440/api/15/job/3a15fbcb-53f3-4cc4-b689-d6f96a9bxxxx' permalink='http://rundeck01:4440/project/test001/job/show/3a15fbcb-53f3-4cc4-b689-d6f96a9bxxxx'>
    <name>てすと01</name>
    <group />
    <project>test001</project>
    <description>てすと詳細</description>
  </job>  
</jobs>

ジョブ実行

# curl -X POST -H 'X-Rundeck-Auth-Token: xxxxxxxxxxxxxxx' "http://rundeck01:4440/api/12/job/3a15fbcb-53f3-4cc4-b689-d6f96a9bxxxx/executions"
<executions count='1'>
  <execution id='6635' href='http://rundeck01:4440/project/test001/execution/show/6635' permalink='' status='running' project='test001'>
    <user>admin</user>
    <date-started unixtime='1455688116789'>2016-02-17T05:48:36Z</date-started>
    <job id='3a15fbcb-53f3-4cc4-b689-d6f96a9bxxxx' averageDuration='985' href='http://rundeck01:4440/api/15/job/3a15fbcb-53f3-4cc4-b689-d6f96a9bxxxx' permalink='http://rundeck01:4440/project/test001/job/show/3a15fbcb-53f3-4cc4-b689-d6f96a9bxxxx'>
      <name>てすと01</name>
      <group></group>
      <project>test001</project>
      <description>てすと詳細</description>
    </job>
    <description>ls -l</description>
    <argstring />
    <serverUUID>xxxxxxxx-400a-403a-828c-xxxxxxxx46ff</serverUUID>
  </execution>
</executions>

※どちらもRundeckのCLIもあります

所感

私はジョブスケジュール設計とか素人なのですが
公式ページにも書いてあったのですが、データドリブンな設計がいいと

様々なコマンドやスクリプトをジョブ化できます。 しかし全てのユースケースに対応するジョブを作ろうとすると、 スクリプトの呼び出し方が少し違う程度のジョブを大量に作ること になってしまうでしょう。 それらの違いとは、往々にして環境やアプリケーションバージョンに関連します。 そのほかの部分については人が必要な情報を与えてジョブを実行しているにすぎません。 スクリプトやコマンドをデータドリブンにしましょう。 そうすればより一般化でき、他のコンテキストでも再利用できます。 同じプロセスの変数をメンテナンスするより、 ジョブが外部データからのオプションモデルで駆動するようにすることで、 よりよい抽象化とカプセル化を期待できます。



ジョブの成功か失敗がファイルを出力できたかどうかというのはキレイな設計な気がします。

IMのまっつさん!構築中のアドバイスありがとうございましたmm

という訳で隣の席の同僚がジョブを作りたくなっているのがうれしいです。
作業をカプセル化して運用が楽になったら幸せです。