Redmine 4.x へのアップグレード作業と検証をK8s上でやる

Redmine 4 にアップグレードしたいが、ローカルで検証しないまま Kubernetes 上でアップグレードの検証をするために試行錯誤した記録。

  • 移行検証のためのテスト環境をローカルに作るのが面倒くさい方
  • サービスの影響範囲が限られていて、数時間停止してもいいやと思っている方
  • Kubernetes 上で動かすことはできたけど、その後のメンテナンスをどうやっていけばいいかわからない方

いずれかに該当する場合は役に立つ情報かもしれません。

前提

  • sammersbn/redmine イメージを使ってRedmineを動かしています。
    • もう何年もお世話になってます。完成度が高くて、これが無いとRedmineをセットアップ・運用したくないレベルの依存度
  • 記事タイトルの通り、Kubernetes ホスト上で Docker コンテナを動かしています。
    • Docker / Docker Compose ならばデータの実体も同一ホスト上にあるので分かりやすいですが、Kubernetes 上では PVC 等を用いてデータの実体は異なるところからマウントしているので、直感的ではない部分もあるでしょう(Kubernetes デプロイに慣れていない場合は)。
    • そのため、Docker コマンドで操作するのとは異なる一癖があります。Kubernets の基本的なお作法を抑えておいたほうが、いざという時に落ち着いて対応できるはず。
  • バックアップはしっかりとっておきましょう。
    • これも、sameersbn/redmine イメージならコマンド1個でバックアップができてしまうので大変お手軽です。

作業前の準備

Redmineのプラグインが少なければ少ないほど、一発成功の可能性は高くなるでしょう。しかし、Redmineを使い込むほどプラグインは増えがちのはず。プラグインが多い場合、ほぼ確実に何かしらひっかかると思います。事前に一通りのプラグインのリストアップ、および最新バージョン・互換性の確認を強く推奨します。

バックアップ

なにはともあれ、まずはバックアップ。いざという時(最終手段)のよりどころです。

kubectl exec でコンテナ内に入り込んで、sameersbn/redmine で用意されているコマンドを実行します。

ちなみにコマンドは、/sbin/entrypoint.sh および、引数として渡すサブコマンドで実行できます。バックアップのためのサブコマンドは app:backup:create です。
https://github.com/sameersbn/docker-redmine/blob/4.1.1-2/entrypoint.sh

## コンテナに入る
kubectl -n <Namespace> exec -it <Podの名前を入力> bash

  # Namespace: 自身の環境に合わせて置き換えてください。特に意識していないならば default です

## バックアップコマンドを実行
/sbin/entrypoint.sh app:backup:create

-- log --
Initializing logdir...
Initializing datadir...
... 中略
Creating backup archive: 1591455718_redmine_backup.tar...
Deleting old backups... (1 removed)

バックアップファイル(tar)は、/home/redmine/data/backups 配下に作成されるので、ローカルにコピーしておきます。うっかりVolumeさえ削除しなければ問題ないはずなので、念の為程度のものです。

なお、1591455718 は今回バックアップをとったタイムスタンプなので、都度変わります。

## リモートからローカルにコピー
kubectl -n <Namespace> cp <Podの名前>:/home/redmine/data/backups/1591455718_redmine_backup.tar 1591455718_redmine_backup.tar

ちなみに、docker-compose ならこんな感じでしょうか。

docker-compose run redmine sameersbn/redmine:<使っているバージョン> app:backup:create

リストアする場合

今回リストアする状況に至りませんでしたが、これも以下の1コマンドでお手軽です。.tarの中身は、コンテンツ(画像とかの)ファイル群、および SQL のダンプなので、サブコマンドに頼らずに1個1個手動でやることも可能です。

docker-compose run redmine sameersbn/redmine:<使っているバージョン> app:backup:restore

詳細は公式ドキュメントを参照してください。

バージョンアップの基本動作

さて本題です。今回のケースでは、Redmine 3.4系からRedmine 4.1系へのアップグレードをすることになりますが、まずは 4.0 系で動くことを確認したあとに、4.1系へのアップグレードとしました。

  1. 既存バージョンでプラグインをアップデートする
    • 面倒臭さとのトレードオフですが、1個1個試したほうが確実です。
    • プラグインをアップデートすると、既存Redmineバージョンは動作対象外になっていて最新Redmineバージョンでしか動かない互換性のケースもあったりするので、事前に提供元サイトを見ておくのが無難でしょう。
  2. プラグインをアップデートしてPodが起動しなくなったら、command: seleep 3600 として空で立ち上げた上でトラブルシュートやデータ補正
  3. 設定を戻して Pod を削除、自動起動のうえ正常性確認
  4. 既存Redmineバージョンで動くことが確認できたら、新Redmineバージョンにアップグレード
    • 操作的には、sameersbn/redmine イメージのタグを書き換えるだけ。直感的です。

以後、2〜4 の繰り返し

sleep している理由

プラグインアップデート後にRedmineが起動しない(すなわち、Railsが起動しない)場合、コンテナはクラッシュしたものと扱われます。そうなると、kubectl exec で Pod 内に入り込むこともできません。それではトラブルシュートができずに困りますので、コンテナ起動コマンド(command)を seleep 3600 で上書きして、とりあえずコンテナが起動できる状態にします。

手段は、手元に Deployment のコンフィグがあるならば、Containers のセクションで command: で上書きして kubectl apply -f しますし、手元になければ kubectl edit deploy ... にてターミナル上で直接編集します(後者の場合、慎重に!)

記載内容は以下の通り。

    spec:
      containers:
      - command:
        - sleep
        - "3600"

command: について:
Define a Command and Arguments for a Container - Kubernetes

sameersbn/redmine イメージの特徴

内容的には sleep しているだけなので、コンテナは何もしていません。この隙に kubectl exec でコンテナ内に入って、プラグインの差し替えやログ確認など、トラブルシュートします。以下の点をおさえておけば、なんとかなるかと思います。

  • /home/redmine/data/plugins に、volumeMounts しているプラグインデータの実体がある
  • 一方で、Redmineコンテナ起動時に使っているプラグインデータは /home/redmine/redmine/plugins にある(/home/redmine/redmine がアプリケーションのROOT)
    • 本来、sameersbn/redmine の起動スクリプト /sbin/entrypoint.sh app:start にて、/home/redmine/data/plugins から /home/redmine/redmine/plugins へ コピーされる
    • sleep で起動している現状は、app:start サブコマンドを実行していないので、データは空
  • データ補正が終わったら /sbin/entrypoint.sh app:start を実行すると、Redmineの起動を試行できる
    • そこで問題なく起動すれば作業完了
    • 何かしらエラーが出る場合は、そのエラーメッセージに準じてプラグインの見直しをする
  • 同じコンテナ内で2回目の app:start を実行をする場合は、2種類の plugins ディレクトリそれぞれ意識する必要がある
    • 大本の /home/redmine/data/plugins だけ弄っても、コピー先の /home/redmine/redmine/plugins に残骸が残っていて「直したはずなのに起動しない(実は変わっていない)」ということが起きるので混乱しないように気をつける

ちなみに参考(プラグインのアップデート)

今回試した環境では以下のPluginを利用しています。GitHubから取得できるプラグインは clone してそのまま配置しているので、アップデートも git pull を実行して一気に最新化する、ちょっと強引な手段をとっています。基本的にはこれで問題は起きていません。

git pull だけで済んだもの

Redmine 4 そのものは 2018-12-09 にリリースされていますので、現在1年半経過しています。なのでオフィシャルレポジトリでの対応も大部分が済んでいるでしょう。逆に今でも終わっていないものはメンテナンスが停滞しているとも言えます。

  • cd ./plugins/redmine_default_custom_query; git pull
  • cd ./plugins/redmine_issue_templates; git pull
  • cd ./plugins/redmine_wiki_extensions; git pull
  • cd ./plugins/view_customize; git pull
  • cd ./plugins/redmine_pivot_table; git pull
  • cd ./plugins/redmine_github_hook; git pull
  • cd ./plugins/redmine_issues_summary_graph; git pull (お試しで入れてるだけで本格利用はしていない)

いくつか、pull が完了しないものがあったりします。これはローカルファイルのパーミッションなんかが変わっていて差分が発生してしまうためです。経験上、この変更が重要だったことはないので、git stash で一時退避したあとに再度 pull して上書きしてしまいます。

オフィシャル対応が済んでいないもの

Redmineはバージョンアップが数年周期に行われるので(メジャーアップは3年?)、メンテナンスが終了・停滞してしまっているプラグインもやはり出てきます。そんなときはまず、GitHubのPull Request を見ましょう。多くの場合は Redmine 4.x 対応のような Pull Request が出されていて、あとは Mergeだけすればいいような状況だったりします。

廃止したプラグイン

この期に整理(削除)したプラグイン達。

  • Easy Gantt (Free版) もセットアップしていたのですが、この期に削除しました。

    • sameersbn/redmine では Plugin ディレクトリにプラグインファイルを配置するだけでDBマイグレートを自動でやってくれるのですが、Easy Gantt に限っては、初期セットアップ時にそれだけでは済まなかった気がします(うろ覚え)。Plugin以外にも rake db:migrate も実行したような・・・。
    • マイグレーションの数は多くないので、最悪手動でメンテすればいいかと、プラグインファイルの削除だけで妥協しました。
  • redmine_agile

    • 見た目かっこよかったので入れていたのですが、無料版だと機能が限定的で、実用的に使うのは少々難しかったので削除。
    • Migrationがあるので redmine:plugins NAME=redmine_agile RAILS_ENV=production しないといけない(やりそびれたので、後でやらないと・・。)
    • https://www.redmine.org/plugins/redmine_agile
  • redmine_tweaks

    • Redmineを使い始めた初期は、かなりお世話になりました。メンテナンスはとっくに終わっていますし、いくつかのプラグインを組み合わせて代替手段も確立できているので削除。
    • Migrationが無いのでプラグインファイルを削除するだけなのですが、このプラグインはRedmine本体のDBにレコードを保存するので、お行儀に少々難あり。Settingだったかのモデル操作をしていて、ファイルを消すだけだと完全とは言えないのですが、実害はないと思うので放っておきます。
    • https://github.com/AlphaNodes/redmine_tweaks

新たにセットアップしたプラグイン

久々に Redmine のプラグインを整理したので、気になっていたけど手を出していなかったものをお試し導入。

Redmine Issue Checklist Plugin

チケット運用をしているとありがちな、「細かいアクションを書きたいのだけど、子チケットを作成するほどでも無いんだよな・・・」というシーンで、GitHubでの [ ] ToDo / [x] Completed ToDo のようなチェックリストを使いたいニーズを解決してくれるもの。

Official Repository は Restream/redmine_issue_checklist: Checklist Plugin creates simple checklists for Redmine issues. ですが、数年更新が止まっていて Redmine 4.x 対応はダメそう(2020/06/07時点)

Redmine 4.x 対応の Pull Request があるので、これを利用しましょう。
Make checklist plugin compatible to Redmine 4.0.4 by lammel · Pull Request #34 · Restream/redmine_issue_checklist

## コンテナ内の Plugins ディレクトリで
git clone -b bugfix/redmine40-compat https://github.com/lammel/redmine_issue_checklist.git

そしてコンテナを再起動して Plugin の Migration が実行されますが、失敗するはずです。

Caused by:
StandardError: Directly inheriting from ActiveRecord::Migration is not supported. Please specify the Rails release the migration was written for:

  class CreateIssueChecklists < ActiveRecord::Migration[4.2]
/home/redmine/data/tmp/bundle/ruby/2.4.0/gems/activerecord-5.2.4.2/lib/active_record/migration.rb:528:in `inherited'
/home/redmine/redmine/plugins/redmine_issue_checklist/db/migrate/20111010202847_create_issue_checklists.rb:1:in `<top (required)>'
/home/redmine/data/tmp/bundle/ruby/2.4.0/gems/activesupport-5.2.4.2/lib/active_support/dependencies.rb:291:in `require'

1ファイル、若干修正する必要があります。redmine_issue_checklist/db/migrate/20111010202847_create_issue_checklists.rb を以下のように修正(追記)します。

## 変更前
  class CreateIssueChecklists < ActiveRecord::Migration

## 変更後
  class CreateIssueChecklists < ActiveRecord::Migration[4.2]

Rails 5 にて導入された Migration Versioning による変更です。
参考: Add migration versioning via Migration subclasses by matthewd · Pull Request #21538 · rails/rails

この変更により、Migrationが成功するでしょう :)

Migrating plugins. Please be patient, this could take a while...
== 20111010202847 CreateIssueChecklists: migrating ============================
-- create_table(:issue_checklists, {:options=>"ENGINE=InnoDB", :id=>:integer})
   -> 0.0300s
== 20111010202847 CreateIssueChecklists: migrated (0.0311s) ===================

Redmine Issue Badge Plugin

akiko-pusu/redmine_issue_badge: Plugin to show the number of assigned issues with badge on top menu.

自身のみ完了チケットを、画面右上に常時バッジで表示してくれます。これいいですね・・・!

これまで工夫として「自身の担当チケット」へのリンクをヘッダーに配置したり、メンバーが少しでもチケットを消化してくれるよう導線を設計してきたのですが、メンバーが多数いると全員への意識付けもそう簡単ではありません。ですが、このように「バッジ」が表示されると、気になる人は潰しにかかってくれる気がします。

「どれがいい」ではなくて、「あらゆる手段で」導線を設けて、健全なチケット運用をしていきたいところ。

redmine-atjs

evolvingweb/redmine_atjs: Integration of redmine with at.js

本文中にチケット間を参照する際に、対象のチケット番号を調べるのって今のRedmineのUIだと面倒くさいですよね・・・。これを使うと # を入力した時点で候補がリストアップされ、文字で絞り込みができるので操作性が激向上しそうです。日本語もOK。

  • Support multi-word searching (IMPORTANT)
  • consider creating AtjsController to provide optimized/customizable autocomplete endpoint. write tests

複数ワードでの補完が重要課題になっているようですが、いまのシングルワードでも十分な実用性だと感じます。

Redmine Indicator

fraoustin/redmine_indicator

少しでも視覚的な情報があれば、チケット消化意識が進まないかなと。バージョンは若いですが、ここ最近いでも Commit が発生しているようなので、期待しつつ様子見。

まとめ

Kubernetes コンテナで動かしているRedmineのバージョンアップや、プラグインアップデートの試行錯誤を記録しました。

今回は手元の開発環境の都合でローカルセットアップができなかった(普段遣いの macOS ハード故障中・・・)ので、直接 K8s ホスト上で実行することにしました。メンテナンスでのダウン時間が許容できるなら特に問題にならないかと思いますが、メジャーバージョンアップはトラブルも多いので、しっかりローカルで検証してから本番に挑みたいですね。

OpenProjectも気になるんですが、やはりsameersbn/redmine イメージをやめるほどのモチベーションはなく、今後もお世話になります。

なお、プラグイン開発やバージョンアップでの考慮点はこちらも参考になります。
Redmine4時代のプラグイン開発 redmine.tokyo #13