ジョブ管理ソフトウェア Rundeck の導入

# はじめに
Unix 系の OS には標準で cron という定期的にジョブ（コマンドやプログラム）を実行するための仕組みがあります。

処理するジョブが少ないうちは cron でどうにかなりますが、ジョブの数が増えてくると以下の様な問題が起こります。

1. ジョブの数が多すぎて管理しきれない。
2. 複数サーバでのジョブ管理が煩雑。
3. ジョブ同士の依存関係の解決が困難。
4. エラー時の監視機能が弱い。

[Rundeck](http://rundeck.org) はこれらの問題を解決可能なツールです。Rundeck は次のような特徴があります。

* Web UI と CLI によるジョブの管理
    * 複数サーバでのジョブ管理を一元化。
        * 大量のジョブの管理が行いやすい Web UI
        * cron と同じく CLI による管理も可能。
    * 実行時間指定の書式は cron ベースなので移行コストも低い。
* ジョブの構造化
    * ジョブの階層構造の定義が可能。
    * 依存関係も定義可能。
* エラー対応
    * ジョブ開始・正常終了・異常終了時にメールやWebHookで通知可能。
    

CI 用のツールである Jenkins で同様の仕組みを構築したことはありますが、 Jenkins 自体がかなり大規模なものであるため、ジョブ管理のみなら Rundeck の導入をおすすめします。

# インストール方法
## 筆者の環境
今回は以下の環境を前提にして進めます。

| 機能       | 名称                                               |
|:-----------|:--------------------------------------------------|
| マシン環境  | Amazon Web Service (AWS) EC2 t2.medium インスタンス |
| OS         | Amazon Linux (2015.09)                            |
| Python     | Python 2.7.10                                     |
| SMTP サーバ | Gmail                                             |

## インストール
以下の流れでインストールします。

1. Java 実行環境（JDK）のインストール
2. yum によるインストール
3. 設定ファイルの変更
4. セキュリティグループの変更
5. 起動
6. 起動後の初期設定

### Java 実行環境（JDK）のインストール
まず以下のコマンドで Java 8 の JDK をインストールします。

```
$ sudo yum install -y java-1.8.0
```

以下のコマンドでインストールされている JDK の一覧が確認できます。

```
$ sudo alternatives --config java

2 プログラムがあり 'java' を提供します。

  選択       コマンド
-----------------------------------------------
*+ 1           /usr/lib/jvm/jre-1.7.0-openjdk.x86_64/bin/java
   2           /usr/lib/jvm/jre-1.8.0-openjdk.x86_64/bin/java

Enter を押して現在の選択 [+] を保持するか、選択番号を入力します:2
```

今回は古いバージョンが既にインストールされていたので2を入力し、最新バージョンに変更します。次のコマンドで現在選択されている Java のバージョンが確認できます。

```
$ java -version
openjdk version "1.8.0_77"
OpenJDK Runtime Environment (build 1.8.0_77-b03)
OpenJDK 64-Bit Server VM (build 25.77-b03, mixed mode)
```

### yum によるインストール
以下のコマンド Rundeck のリポジトリを登録後、yum で Rundeck をインストールします。

```
$ sudo rpm -Uvh http://repo.rundeck.org/latest.rpm
$ sudo yum install -y rundeck
```

### 設定ファイルの変更
`/etc/rundeck/rundeck-config.properties` の以下の行をインストールしたサーバのIPアドレスもしくはホスト名に書き換えます。

編集前

```
grails.serverURL=http://localhost:4440  
```

編集後

```
grails.serverURL=http://XXX.XXX.XXX.XXX:4440  
```

### セキュリティグループの変更
Inbound TCP 4440 へのアクセスを許可する必要があるため、AWS のコンソールより以下の設定を変更します。

```
   EC2
-> Security Groups
-> (対象のマシンの) セキュリティグループを選択
-> Inbound タブ を選択
-> Edit
-> Add Rule
```

ルールは次の通りに入力します。

* Type: Custom TCP Rule
* Protocol: TCP
* Port Range: 4440
* Source: Anywhere (必要に応じて特定のIPを指定する)

`Save` を選択してルールを保存します。

### 起動
以下のコマンドで Rundeck を起動します。

```
$ sudo service rundeckd start
```

設定ファイルで設定したURL (`http://XXX.XXX.XXX.XXX:4440/`) にアクセスすると、ログイン画面が確認できます。

デフォルトでは User / Password ともに `admin` でログインできます。

### 起動後の初期設定
管理者ユーザのパスワードを変更します。`/etc/rundeck/realm.properties` に次の形式で記述されています。

`<username>:<password>[,<rolename> ...]`

デフォルトだと平文で記述されていますが、セキュリティ上これは良くない状態なので、以下のコマンドで暗号化した文字列を作成します。今回はついでに初期パスワードから新しいパスワードに変更しています。

```
$ java -cp /var/lib/rundeck/bootstrap/jetty-all-7.6.0.v20120127.jar org.eclipse.jetty.util.security.Password admin [任意のパスワード]
[任意のパスワード]
OBF:XXXXXXXXXXXXXXXXXXXXXXXX
MD5:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
CRYPT:XXXXXXXXXXXXX
```

出力された3つの文字列のうち一つを選択し、`<password>` の部分に記述します。以下のように admin ユーザーに関する行を書き換えます。

```/etc/rundeck/realm.properties`
admin:MD5:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX,user,admin,architect,deploy,build
```

一度ログアウトし、再ログイン時に上記のパスワードを入力すると新しいパスワードでログイン可能になります。

また、エラー時にメールで通知の設定もここで行います。

デフォルトの `/etc/rundeck/rundeck-config.properties` だと一部設定が記述できないので、Groovy の設定ファイルに変更し以下の内容を追記します。

```
$ sudo mv /etc/rundeck/rundeck-config.properties /etc/rundeck/rundeck-config.properties.bk
```


```/etc/rundeck/rundeck-property.groovy
loglevel.default="INFO"
rdeck.base="/var/lib/rundeck"
rss.enabled=false
dataSource {
        dbCreate="update"
        url="jdbc:h2:file:/var/lib/rundeck/data/rundeckdb;MVCC=true;TRACE_LEVEL_FILE=4"
}

grails {
        serverURL="http://ホスト名:4440"
        mail {
                host="smtp.gmail.com"
                port = 465
                username="XXXXXX@gmail.com"
                password="XXXXXXX"
                props = ["mail.smtp.auth":"true",
                      "mail.smtp.socketFactory.port":"465",
                      "mail.smtp.socketFactory.class":"javax.net.ssl.SSLSocketFactory",
                      "mail.smtp.socketFactory.fallback":"false"]
        }
}
```

また、`/etc/rundeck/profile` を以下のように書き換えます。

```/etc/rundeck/profile(before)
...
        -Drundeck.config.location=/etc/rundeck/rundeck-config.properties \
...
```


```/etc/rundeck/profile(after)
...
        -Drundeck.config.location=/etc/rundeck/rundeck-property.groovy \
...
```

以下のコマンドでサービスを再起動します。

```
$ sudo service rundeckd restart
```

# 使い方
## プロジェクトの作成
今回はサンプルとして `sample-project` というプロジェクトを作成します。`New Project` をクリックすると新規プロジェクト作成画面になります。

以下の項目を設定します。

* Project Name
  * `sample-project` を入力。
* Description
  * `This is a sample project.` を入力。

他は特に変更する必要なし。別マシンでジョブを実行したい場合は、`Default Node Executor` や `Default Node File Copier` で SSH や SCP に用いる認証鍵の設定する。

その後 `Create` をクリックしプロジェクトを作成。

## 新規ジョブの作成
以下のジョブを作成してみる。

1. echo コマンドで画面に文字列を定期的に出力する
2. 引数ありの Python スクリプトの実行
3. 実行時にエラーになる Python スクリプトを実行しメール通知を行う

## echo コマンドで画面に文字列を定期的に出力する
毎分 0 秒に `Hello Job!` を出力するジョブを作成してみる。Create New Job を選択し、以下の通りに入力する。

* Job Name:
  * `echo sample` を入力。
* Decription:
  * `echo コマンドで画面に文字列を定期的に出力する。` を入力。
* Workflow:
  * `Node Steps` で `Local Command` を選択。
    * Command に `echo "Hello Job!"` を入力。
    * Step Description に `echo step` を入力。
  * `Save` で保存。
* Schedule to run repeatedly?:
  * `crontab` を選択し、`0 * * * * ? *` を入力。

他はデフォルトのままでよい。ページ下部の Create をクリック。これで毎分0秒に `Hello Job!` が出力される状態となった。実行結果は `Activity for this Job -> recent -> (ジョブID)` で確認できる。 `Log Output` を選択すると、 `Hello Job!` と出力されたのが確認できる。

動作確認が終了したら、定期実行を解除する。`Action -> Edit this Job` を選択し、`Schedule to run repeatedly?` を `No` にして画面下部の `Save` をクリックする。これでジョブ詳細画面の `Run Job Now` をクリックしないかぎり再実行されなくなった。

## 引数ありの Python スクリプトの実行
文字列を1つ引数として受け取り、"こんにちは [入力した引数] さん！" を出力する Python スクリプトを実行するためのジョブを作成する。

ますサーバ内で以下のスクリプトを作成する。尚使用している Python はデフォルトの 2 系を前提としていますが、Python 3 を用いる場合は `print("XXX")` としてください。

```
$ sudo mkdir -p /work/arg_print
$ sudo chmod 777 /work
$ sudo chown rundeck /work/arg_print
```

以下のスクリプトを作成し、所有者を `rundeck` ユーザにします。

```/work/arg_print/arg_print.py
#!/usr/bin/env python
#-*- coding:utf-8 -*-
import sys
print "こんにちは " + sys.argv[1] + " さん！"
```

```
$ sudo chown rundeck /work/arg_print/arg_print.py
```

次はジョブを作成します。Webのインターフェースから、プロジェクト内で `Job Action -> New Job` を選択します。

* Job Name:
  * `python arg sample` を入力。
* Decription:
  * `"こんにちは [Name] さん！" を出力するジョブ。` を入力。
* Options:
  * `Add an option` を選択。
     * Option Name: `Name`
     * Description: `User name`
     * Default Value: `Admin`
     * Required: `Yes`
  * 上記を入力後 `Save` をクリック。
* Workflow:
  * `Node Steps` で `Local Command` を選択。
    * Command に `python /work/arg_print/arg_print.py ${option.Name}` を入力。
    * Step Description に `arg print step` を入力。
  * `Save` で保存。

他はデフォルトのままでよい。ページ下部の Create をクリック。`Run Job Now` をクリックすると、`Name` に `"Admin"` が代入されて `こんにちは Admin さん！` が出力される。

ジョブの詳細ページで `Name` の項目をデフォルトの `Admin` から `RUNDECK` に変更し `Run Job Now` をクリックすると、`Name` に `"RUNDECK"` が代入されて `こんにちは RUNDECK さん！` が出力される。

## 実行時にエラーになる Python スクリプトを実行しメール通知を行う

```
$ sudo mkdir -p /work/error_sample
$ sudo chown rundeck /work/error_sample
```

以下のスクリプトを作成し、所有者を `rundeck` ユーザにします。

```/work/error_sample/error_sample.py
#!/usr/bin/env python
#-*- coding:utf-8 -*-
raise ValueError("sample")
```

```
$ sudo chown rundeck /work/error_sample/error_sample.py
```

次はジョブを作成します。Webのインターフェースから、プロジェクト内で `Job Action -> New Job` を選択します。

* Job Name:
  * `python error sample` を入力。
* Decription:
  * `例外処理時のメール送信テスト。` を入力。
* Workflow:
  * `Node Steps` で `Local Command` を選択。
    * Command に `python /work/error_sample/error_sample.py` を入力。
    * Step Description に `error step` を入力。
  * `Save` で保存。
* Send Notification?:
  * `Yes`
  * On Failure:
    * Send Email: 送信先のメールアドレスをカンマ区切りで指定。
    * Subject: `Python error test`
    * Attach output log: チェック状態にする。

ページ下部の Create をクリック。その後 `Run Job Now` をクリックするとジョブが実行され、エラー内容の通知が送信先のメールアドレスに送られます。


# 終わりに
今回は以下の項目については割愛しています。

* 他サーバ上でのジョブ実行方法。
  * 秘密鍵を使った SSH の通信が通るよう設定し、秘密鍵ファイルを登録すれば可能になります。
* 複数ジョブの連続実行
* ジョブの繰り返し設定

ただしこれらの設定は直感的にGUIで設定可能です。
Rundeck を活用して、快適タスク管理生活をエンジョイしましょう。


# 参考文献
* [Rundeck](http://rundeck.org)
* [Rundeck - cronから移行しやすいジョブスケジューラを使ってみよう](http://heartbeats.jp/hbblog/2015/01/rundeck.html)