Azkabanというジョブスケジューラーを最新(4.x)にアップデートするまでの作業メモ

今回私が担当しているAzkabanのバージョンアップが必要になったため、
アップグレードするための検証と作業メモを投稿します。


## はじめに
私が担当したAzkabanは、10年近く前に構築されたものであり、使用しているバージョンは2.5.0でした。
最初に他のジョブスケジューラーの検討や、バージョンアップ以外の対応も検討しましたが、
アップグレードで対応するのが最も簡単であると判断しました。
また、古いバージョンのシステムが今まで動いていた実績もあり、構成を変えたくないという気持ちもありました。
Azkabanの機能については、[公式](https://azkaban.github.io/)を確認ください。


## なぜバージョンアップが必要になったか
主な動機は、以下の2点です。

- 動作している2.5.0ではメール送信時にTLSの設定ができなかったため、バージョンアップすることで対応できると考えました。
- 3.x系にするのも、4.x系にするのも大したコストの差がないため、最新版にすることにしました。


## 調査
### AzkabanはTLS対応しているのかを調べる
Azkabanのメール設定方法はDocに記載があり、設定ファイルで
`mail.tls=true`を設定すればいいということがわかります。
https://azkaban.readthedocs.io/en/latest/configuration.html?highlight=tls#notification-email-properties
なので簡単だと思っていました。


### 最新版のリリースされているazkabanを確認
azkabanの安定版は何処にあるのか確認します。
公式のリンクに沿っていくと、githubページで管理されているようでした。
https://github.com/azkaban/azkaban/releases
おそらく4.0.0の段階で作られたものです。
ここで公開されているのは成果物ではなく、単純にソースコードをパッキングしたものでした。
なので、手動でBuildする必要がありました。
(tagうちされているので、普通にgithubでも確認することが出来る)

手動でビルドするのは手間ですが、それしか提供されていないのであれば仕方がありません。
azkabanはjavaで動作していて、buildにはgradleを利用しています。


### Javaでメール送信時にTLS1.2対応するにはどうするかを調べる
TLSの設定は設定ファイルで可能なのはわかりましたが、古いTLSのバージョンで通信されてしまうと無駄骨なので、TLSのバージョン指定の方法を調べました。
azkabanはjava言語のアプリなので実装又は、起動パラメータで設定できるだろうと考えました。
(ここを調べておいてよかった)

ググれば一発で出てきますが、javaでメール送信する場合は
javax.mail:1.6.2を使うという事がわかりました。
https://stackoverflow.com/questions/47166425/how-to-force-javamailsenderimpl-to-use-tls1-2

そこでソースで使用しているlibを確認してみたところ古いバージョンを利用している事がわかりました。
https://github.com/azkaban/azkaban/blob/4.0.0/build.gradle#L111

### 回避方法
これが自身のPJであれば、依存関係を差し替えてbuildするだけで解決するのですが、私のポリシー的に、可能な限り手を入れるという選択肢は選択しない方向性で調査しました。

きっと同じ事で頭を抱えた人は居ただろうと思い、githubでissueを検索してみた所発見しました。
https://github.com/azkaban/azkaban/pull/3034

このPRは既にマージされているのですが、リリースはされていないということがわかりました。
これが原因で最新版のソースからbuildする方法を選択せざるを得なくなりました。
(いつかはリリースされるとは思いますが、調査段階でいつリリースされるかわからないものを待てなかった)

### 最新のAzkabanにするためには
最初2.5 -> 4.x にするというのは結構大変そうに思えましたが、Docを見てみるとUpgrade方法が書いてあったので、多分大丈夫だろうと判断出来ました。
しかし、実は3.xからazkabanはjobの定義記述を大きく変更したアップデートが入っていたみたいです。
https://azkaban.readthedocs.io/en/latest/createFlows.html

最初このページを見た時に、既存のjobを全て書き直すのは大変だと思いましたが
docの先頭に以下の記載がありました。
```
This section covers how to create your Azkaban flows using Azkaban Flow 2.0. Flow 1.0 will be deprecated in the future.
```

ようはまだ古いjob定義使えるよ!と書いてありました。
もし古い定義がそのまま使えるのであれば、作業は簡単になると考えたため、実際に検証することにしました。

# 検証作業
まずは最新版のazkabanを動作させる環境をlocalで構築し、色々検証する事にしました。

### GOAL
検証の目的をまず定めました。

- 古いJOB定義が使えるかどうか検証
- アップグレード手順の確立

### ビルドしてみる
ソースからbuildする必要があるので、buildが出来る環境を用意する必要があります。

- JDK 1.8以上
  - [readme](https://github.com/azkaban/azkaban/blob/master/README.md#build)には8以上と記載されています
- Node(nmp)
  - もしかすると不要かもしれないが、フロントでnode_moduleを利用しているので、入れておくといいかもしれない
    - 私の環境は既にnode(npm)が動作する環境だったので詳細不明

以上の環境を作った上で、README.mdに記載されている方法でbuildします。

```
./gradlew installDist
```

すると、残念なことにエラーが出ました。

```
> Task :azkaban-web-server:nodeSetup FAILED

* What went wrong:
Execution failed for task ':azkaban-web-server:nodeSetup'.
> Could not resolve all files for configuration ':azkaban-web-server:detachedConfiguration1'.
   > Could not resolve org.nodejs:node:8.10.0.
     Required by:
         project :azkaban-web-server
      > Could not resolve org.nodejs:node:8.10.0.
         > Could not get resource 'https://nodejs.org/dist/v8.10.0/ivy.xml'.
            > Could not GET 'https://nodejs.org/dist/v8.10.0/ivy.xml'. Received status code 403 from server: Forbidden
```

そんな馬鹿な、、、と思い探してみると確かに存在しない。
調査してみると、どうやらurlが何かのタイミングで変わったようです。
https://github.com/srs/gradle-node-plugin/issues/292

もしかすると、何処かのタイミングでURLの置き換えがmasterにマージされるとは思いますが、今はbuildさせる必要がありました。
解決方法は2つ思いつきました。
- 1.azkaban-web-serverのbuild.gradleを直す
- 2.どうにかしてurlを動的に切り替える

1の手段で変更してBuild出来ることは確認した上で、先程もお話したように私のポリシー的に出来れば手を入れたくありません。
ではどうするかというと、azkabanのbuildにはgradleを利用しているのですが
gradleには[Initialization Scripts](https://docs.gradle.org/current/userguide/init_scripts.html)という機能があり、それを利用して回避できないかと私は考えました。


### Initialization Scriptsで、urlの書き換えをしてみる
色々試行錯誤(1日分ぐらいは悩みました)した結果このようなinit-scriptを作成することにより、ビルドが出来るようになります。
✳gradleの知識が十全ではないため、誤ったscriptかもしれません

init.gradle
```
gradle.afterProject { pj ->
    if (pj.name == "azkaban-web-server") {
        pj.extensions.configure("node", {
            def nodeExtension = pj.extensions.findByName("node")
            nodeExtension.distBaseUrl = "https://direct.nodejs.org/dist/"
            return nodeExtension
        })
    }
}
```

ビルドするときにはこのようにします。

```
./gradlew -I init.gradle installDist
```

### 起動してみる
jdk1.8以上の環境をサクッと作るためにdockerを利用します。
私はeclipse-temurin:8-alpineのイメージで試してみました。

```
docker run -it --rm -p 8081:8081 -v ".:/root/azkaban-solo-server" -w "/root/azkaban-solo-server" eclipse-temurin:8-alpine /bin/ash
```

これでコンテナの中に入り、起動scriptを実行します。
✳初期設定では、H2をdatabaseにしてアプリが起動するため、必要に応じて変更してください。

初期設定ではアカウントは管理用アカウント1つしか無いので、とりあえずそれを入れる。
✳confの中を見たらわかります
![loginpage](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/136895/03325406-d88c-b0ec-f43c-4769d955d5fd.png)

ログイン出来たら、各機能の確認をしました。
見た目はそこまで変更は入っていなかったのですが、**スケジュール**に関して大きな変更が入っていました。

![schedule](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/136895/5e9e86ca-b056-62a0-687a-4d939dfcf2fd.png)

以前までは年月日、時間、リピート期間だったのですが、なんと最新版にするとcron形式の指定に変化していました。
https://azkaban.readthedocs.io/en/latest/useAzkaban.html#schedule-flow

既存のスケジュールから移行するとどうなるんだろう、、、と不安になり検証してみた所
2.5から登録されているスケジュールは、最新にしても引き継がれるが、1度スケジュールが実行されると、登録されていたスケジュールが消える。
という結果になりました。
そのため、移行後は速やかにスケジュールを登録し直す事をオススメします。

### 古いJOB定義がそのまま最新版で使えるかどうか検証
普通にアップロードすると動きました。
2.x時に登録しているjobはそのまま使えることも確認しました。

### 既存のDBからのUpgradeの場合
azkaban標準機能で、最新のテーブル構造に自動的に更新されると思っていたのですが、いくつか手動でscriptを実行する必要があったため、細かく記載しておきます。

✳ちゃんとソースを見たわけじゃないですが、おそらく[ここ](https://github.com/azkaban/azkaban/blob/master/azkaban-db/src/main/sql/database.properties#L1)の定義が4.xになっていないので、upgradeのマイグレーションが実行されなかった可能性が高いのですが、自動upgradeの動作が不安定なため、止めているという可能性もある為気にしないようにします。



#### 2.x系 -> 3.x系
公式Docを見ると古いversionからupgradeする場合の手順が記載されています。
https://azkaban.readthedocs.io/en/latest/getStarted.html#upgrading-db-from-2-7-0

が、ここで記載されている下記のscriptファイルは最新のソースからは消えています。

- update.active_executing_flows.3.0.sql
- update.execution_flows.3.0.sql

ではどうするかというと、3.0.0のタグでcheckoutすると該当ファイルを見つける事が出来たので、事なき終えました。(多分このファイルを探すのに1時間はかかりました)
✳まぁこんな古いVersionからUpgradeするユーザーなんて居ないので、誰も困っていなかったんだと思います。

#### アプリを起動する
この段階では最新のazkabanは起動しないです。
原因はDBのマイグレーションを行っていない為です。
何故か次の工程を先に行うと、エラーになってしまったので、この手順にしています。

#### 3.x系 -> 4.x系
下記のディレクトリにあるupgrade.*という名の付いたスクリプトを適用させる必要がありました。
`build/install/azkaban-solo-server/sql`

また、下記のファイルに関しては、中身を手動で変更する必要があるので注意してください。
`upgrade.3.43.0.to.3.44.0.sql`


本来であれば、これだけで済むはずなのですが、これだけではまたアプリケーションが起動しませんでした。
起動後のログから問題箇所を確認し、scriptを実行させる必要がありました。

```sql
ALTER TABLE projects
  ADD COLUMN security_tag VARCHAR(64);
```

ちなみにこのscriptはこのファイルに記載されていました。
マイグレーションファイルの作成をサボったのか、、、?と推測。
✳こういうのはcommitログ見てみると面白いかもしれません。
https://github.com/azkaban/azkaban/blob/master/azkaban-db/src/main/sql/create.projects.sql#L17-L18

上記の手順を完了すると、アプリが正しく起動出来るようになりました。


# 移行手順
本作業は2.5.0から、最新の4.x系にアップグレードを行うための手順を記載します。

また、jobの定義は最新の定義に更新した方が適切だが、まだ現状のversionだと既存のjob定義で動作が出来るので、今回はそのまま使うことにする。(工数の関係)

### 前準備
#### ソースをbuildする
[ビルドしてみる](#ビルドしてみる) を参照

#### 設定ファイルを書き換える
ビルドが成功すると下記のディレクトリに配置に必要なファイルが生成されています。
`azkaban-solo-server/build/install/azkaban-solo-server`
生成されたファイルの設定ファイルを本番環境に合わせて設定してください。
新しく環境を構築される方は必要なところだけ適時設定してください。
既存のDBをそのまま使う場合は、databaseあたりの設定をしてください。
また、メール送信時にTLSを利用する想定の場合は、下記の設定を付与する必要があります。
`mail.tls=true`

#### 起動スクリプトを変更する
起動スクリプトはbuildするとinstallディレクトリに生成されるのですが、このscriptを使うとログファイルが、アプリケーションディレクトリに出力されてしまいます。
この設定で問題ない場合はスルーしてください。
https://github.com/azkaban/azkaban/blob/master/azkaban-web-server/src/main/bash/start-web.sh#L1-L5

基本ログは/var/log/配下に置きたいと言うポリシーで行う場合は、別途scriptを作りましょう。

#### 既存のDBからのUpgradeの場合
[既存のDBからのUpgradeの場合](#既存のDBからのUpgradeの場合) を参照
念のためbackupを取っておくことを推奨します。


## 移行手順概要
ざっくりまとめるとこの手順を行うと良いと思います。

- 1.既存のazkaban管理画面にログインして、スケジュールの設定をスクショしておく
- 2.既存のazkabanが動作しているサーバーにログイン
- 3.手元でbuildした成果物(azkaban-solo-server)をサーバーに配置
- 4.既存のazkabanを停止し、最新のazkabanを起動
- 5.マイグレーションを適用
- 6.もう一度azkabanを起動
- 7.azkabanの管理画面にログインして動作しているのを確認
- 8.スケジュール画面ですべてのスケジュールを設定し直す

## 移行手順詳細
必要な分だけ記載

### 1.既存のazkaban管理画面にログインして、スケジュールの設定をスクショしておく
以前どんなスケジュールを登録していたかが分からなくなってしまわないようにという意図です。

### 6.もう一度azkabanを起動
マイグレーションが適用されれば、問題なくアプリケーションが起動するはずです。
ただし、起動しなかった場合は、起動時のログを確認して、問題の原因を特定してください。


以上で移行が完了すると思います。
疲れた。