開発支援サーバー(GitBucket、Redmine、Jenkins)を退役させてSaaSに移行させた話

f:id:vasilyjp:20190225094425j:plain

こんにちは。ZOZOテクノロジーズ開発部の田島です。 今時のシステム開発ではさまざまなツールを利用することが当たり前になっています。 そして各種ツールは日々新しいものが開発され、今まで当たり前だったものがレガシーなツールと呼ばれることも珍しくありません。

弊社では、GitHubやCircleCI・Slackなど様々なツールを利用しています。 私達のチームでもこれらのツールを利用していますが、それ以外にもGitBucketやJenkins・Redmineを独自で管理し利用していました。

今回ある理由からそれらのツールをSaaSへ移行しました。その経緯と移行手順を紹介します。

概要

開発支援サーバの紹介

利用しているGitBucket・Jenkins・Redmineは開発支援サーバと呼ばれる一台のEC2インスタンスの上で動作していました。

f:id:vasilyjp:20190215101934p:plain

やったこと

これらのツールを以下の図のように、「GitBucketをGitHub」・「JenkinsをCircleCI」・「RedmineをGitHub issue」へ移行しました。

f:id:vasilyjp:20190215102002p:plain

ツール移行の経緯

なぜ開発支援サーバーが必要だったのか

まず開発支援サーバーが必要だった理由を紹介します。以下のような理由から開発支援サーバーの運用を行っていました。

  • プロジェクト発足当時、全社的に利用するツールが統一されていなかった
  • セキュリティのルールが定まっていなく、SaaSを利用することがためらわれていた
  • 開発支援サーバを利用しているプロジェクトは外部委託しているものだったためツールをコントロールできなかった

なぜ移行を行ったのか

次になぜツールの移行を行ったのかを紹介します。以下のような理由から開発支援サーバーのツールの移行を決意しました。

  • 全社的に利用するツールの統一が行われ始めた
  • プロジェクトをまるごと内製化するため自分たちでツールをコントロールできるようになった
  • 同じような役割のツールが複数存在し、情報が分散されてしまっていた
  • EC2に開発支援サーバーを立てているためデータのバックアップなどすべて自分たちでやる必要があった
  • サーバーがダウンすると3つすべてのツールにアクセスできなくなる

最も大きな理由としては、全社的に利用するツールが統一され始めたこと。外部委託をしていたプロジェクトをまるごと内製化し自分たちでツールをコントロールできるようになったことが主な動機となりました。

移行先の選定

それぞれのツールの移行先として、様々なツールを検討しました。最終的にどのような理由から移行先のツールを決定したのかを紹介します。

GitHub

GitBucketの移行先をGitHubにした理由は以下になります。

  • 全社的にリモートリポジトリがGitHubに統一された
  • 開発者全員が利用したことのあるツールである
  • 権限管理などが柔軟
  • SaaSのため自分たちで管理の必要がない

基本的には社内でリモートリポジトリがGitHubに統一されたことが大きな要因になりました。

CircleCI

Jenkinsの移行先をCircleCIにした理由は以下になります。

  • 全社的にCircleCIが利用されている
  • チームメンバーがCircleCIに慣れている
  • 設定をコードで管理できる
  • Jenkinsでやっていたことがすべて実現できる
  • SaaSのため自分たちで管理の必要がない

チームメンバーがCircleCIに慣れており、かつやりたいことを柔軟に実現できたことが採用の大きな要因になりました。

GitHub issue

Redmineの移行先をGitHub issueにした理由は以下になります。

  • 進行中のタスクと完了したタスクを分けることができる
  • 新たにツールの導入が必要ない
  • 検索ができる
  • SaaSのため自分たちで管理の必要がない

全社的にドキュメントはConfluenceにチケット管理はJiraに統一しているので、はじめはどちらかに移行することを検討しました。そしてRedmineの移行先の条件としては進行中のタスクと完了したタスクを分けることのできるツールが好ましいと判断しました。Jiraはこの条件に当てはまりますが、Redmineでは一部の作業履歴などドキュメントのように利用していました。また、Jiraでは工数管理も同時に行っているため移行先として好ましくないと判断しました。そこで今までの作業履歴が参照できれば良い程度の要件だったため、GitHub issueが適当であると判断しました。

ツールの移行

移行手順

ツールの移行は以下の順番で行いました。

  1. GitBucket
  2. Jenkins
  3. Redmine

まずはじめに、GitBucketの移行を行いました。Jenkinsの移行先であるCircleCIではGitHubとの連携を考えていました。そこで先にリポジトリをGitHubを移行する必要がありました。次にJenkinsを移行します。Redmineはどちらのツールにも依存をしていないため最後に移行をしました。以下ではそれぞれのツールの移行手順を紹介します。

GitBucket to GitHub

最初にGitBucketからGitHubへの移行手順を紹介します。 今回移行が必要だったリポジトリは10個ほどだったので無理にスクリプトを作成したりツールを利用せず手作業で移行を行いました。

移行作業では「Gitリポジトリの中身を、ブランチとタグも含めて別リポジトリにコピーする」の記事を参考にさせていただきました。

手順は以下の通りです(1〜4は上の記事通りに進めました)

  1. GitBucketのリポジトリを手元にcloneする
  2. すべてのブランチ・タグを取得
  3. リモートリポジトリにGitHubを追加する
  4. GitHubにコンテンツをpush
  5. GitBucket上で作成されていたPull RequestをGitHub上で作成
  6. Jenkinsの向き先をGitHubへ変更する

1. GitBucketのリポジトリを手元にcloneする

まず手元に移行が必要なGitBucket上のリポジトリをCloneします。

$ git clone GitBucketのリポジトリ

2. すべてのブランチ・タグを取得

次に全てのブランチ・タグを取得します。

$ git branch -r | grep -v "\->" | grep -v master | while read remote; do git branch --track "${remote#origin/}" "$remote"; done
$ git fetch --all
$ git pull --all

3. リモートリポジトリにGitHubを追加する

リモートリポジトリにGitHubを追加します。origin の部分はGitBucketと同じものにして上書きしても別にしても作業しやすいものを指定してください。

$ git remote set-url origin GitHubのリポジトリ

4. GitHubにコンテンツをpush

GitHubにコンテンツをpushします。

$ git push --all origin
$ git push --tags

5. GitBucket上で作成されていたPull RequestをGitHub上で作成

GitBucket上で作成されていたPullリクエストをGitHubで作成します。
こちらも、数が多くなかったので手動で行いました。

6. Jenkinsの向き先をGitHubへ変更する

利用しているJenkinsではGitBucketからコードをPullしてビルドを行っていました。 次の章でJenkinsをCircleCIに移行しますが、移行が完了するまでJenkinsのリポジトリの参照先をGitHubへ変更する必要があります。 以下がその手順です。

GitHub用のデプロイキーを作成

github用のデプロイキーをリポジトリの数だけ発行します。以下では github-repository1github-repository2 リポジトリ例でのデプロイキー作成の例になります。

$ ssh-keygen -l -f github-repository1.pem
$ ssh-keygen -l -f github-repository2.pem

.ssh/configにHostを追加

~/.ssh/configの設定を発行したデプロイキーの数だけ追加します。

Host github-repository1
  User git
  Port 22
  HostName github.com
  IdentityFile ~/.ssh/github-repository1.pem
  TCPKeepAlive yes
  IdentitiesOnly yes

Host github-repository2
  User git
  Port 22
  HostName github.com
  IdentityFile ~/.ssh/github-repository2.pem
  TCPKeepAlive yes
  IdentitiesOnly yes

GitHubへデプロイキーを配置

GitHubの各リポジトリのSettingsでデプロイキーを登録します。

f:id:vasilyjp:20190215102127p:plain

Jenkinsの設定を変更

最後にJenkinsのプロジェクトの設定から、ソースコード管理の欄を変更します。 Repository URLには先ほど。 .ssh/config で設定したHost名を利用します。またCredentialsにはデプロイキーを発行したユーザーと紐付いたものを利用します。

f:id:vasilyjp:20190215102140p:plain

それぞれの項目の例は以下のようになります。

RepositoryURL: git@github-repository1:user_name/repository_name
Credentials: Jenkins

Jenkins to CircleCI

次にJenkinsをCircleCIへ移行します。今回Jenkinsで扱っているプロジェクトはすべてMavenで管理されたJavaアプリケーションでした。 そこで、以下の手順で移行を行いました。

  1. Jenkinsでやっていることを確認
  2. JenkinsでやっていることをCicrcleCIで再現
  3. CircleCIによってビルドされたファイルの動作確認

1. Jenkinsでやっていることを確認

まずはじめにJenkinsでやっていることを確認する必要があります。 Jenkinsの各Mavenプロジェクトの「設定」で何をやっているのか把握を行いました。

1. Mavenでやっていることを確認

Jenkinsで扱っていたプロジェクトはすべてMavenプロジェクトのためビルド項目の「ルートPOM」と、「ゴールとオプション」を調べます。これによりJenkinsで実行されるMavenのゴール・オプションがわかります。以下の例では、単純にmvn clean package を行っていることがわかります。

f:id:vasilyjp:20190215102204p:plain

2. JDKのバージョン確認

JavaのバージョンをCircleCI移行時に合わせる必要があるので、ビルド時のJDKのバージョンを調べておく必要があります。以下の例では、「OpenJDK 1.8」が利用されていることがわかりました。

f:id:vasilyjp:20190215102215p:plain

3. ビルド・トリガの確認

どのようなタイミングでビルドが行われるかを確認します。私達のプロジェクトでは以下のように、定期的にビルドを実行するような設定になっていました。

f:id:vasilyjp:20190215102225p:plain

4. 成果物の確認

最後にJenkinsの実行によって生成される成果物を確認します。CircleCI上でも同じように成果物を出力する必要があるため調べておく必要があります。

f:id:vasilyjp:20190215102237p:plain

2. JenkinsでやっていることをCicrcleCIで再現

次にJenkinsでやっている内容をCircleCIで再現します。以下がその例になります。やっている内容はコメントをご参照ください。

version: 2.1

jobs:
  build-job:
    # Jenkinsサーバーがcentos7だったため同じOSを利用する
    docker:
      - image: centos:7
        user: root
    working_directory: ~/repo
    # Jenkinsサーバーと同じ環境変数をセット
    environment:
      LANG: ja_JP.UTF-8
      LANGUAGE: ja_JP:ja
      TZ: Asia/Tokyo
    steps:
      - checkout
      - run:
          name: 'Setup'
          command: |
            cp /usr/share/zoneinfo/Japan /etc/localtime
      # OpenJDKとMavenをインストール
      # localtimeの設定
      - run:
          name: 'Install Dependencies'
          command: |
            yum -y install java-1.8.0-openjdk
            yum -y install maven
      # 依存ライブラリをローカルにダウンロード
      # NOTE: mvn dependency:go-offline command has a bug. Refer to https://issues.apache.org/jira/browse/MDEP-516
      # - run:
      #     name: 'Install dependency maven'
      #     command: mvn dependency:go-offline
      # テストを実行
      - run:
          name: 'Test'
          command: mvn clean test
      # パッケージング
      - run:
          name: 'Package'
          command: mvn package -Dmaven.test.skip=true
      # 成果物をCircleCI上の画面からダウンロードできるようにする
      - run:
          name: 'Set Artifacts'
          command: |
            mkdir /tmp/artifacts
            cp artifact.jar /tmp/artifacts
      - store_artifacts:
          path: /tmp/artifacts

workflows:
  version: 2
  build-deploy:
    jobs:
      - build-job

Jenkinsでは mvn clean package のみを行っていました。しかし、CircleCIでは失敗した場合にどのタイミングで失敗したのかがわかりやすいようにテストとパッケージングを別々にしました。また、依存ライブラリのインストールも別にしたかったのですが問題が生じて断念しました。1つのプロジェクトで複数のMavenプロジェクトに分けている場合 mvn dependency:go-offline で失敗してしまいます。詳細はこちらのissueをご参照ください。

3. CIによってビルドされたファイルの動作確認

JenkinsでビルドしたjarとCircleCIでビルドしたjarを100%同じものにすることはできませんでした。そこで動作確認を開発環境やステージング環境できちんと検証する必要があります。検証ができたものから順次本番のjarをCircleCIでビルドされたものに切り替えて行きます。

Redmine to GitHub issue

最後にRedmineをGitHub issueへ移行する方法を紹介します。手順は以下のようになります。

  1. 移行のためのツールを探す
  2. RedmineとGithubの設定をする
  3. 移行ツールをfetchして自分好みにカスタマイズ
  4. redmine2githubを実行

1. 移行のためのツールを探す

Redmineのチケットは300ほどあったため、流石にこれを手作業で移行すると日がくれてしまいます。そこで、これに関しては自動化することを検討しました。 今どきRedmineからどこかに移行するというニッチなツールが無く、最初は自分で作ろうと思いました。しかし、redmine2githubという素晴らしいツールのおかげで最小限の力で移行を実現できました。

以下がredmineからGitHubへ移行するためのツールで、Ahmy YulrizkaさんがGitHubで公開しています。利用するタイミングではライセンスが書かれていなかったのですが、事情を説明したところ丁寧にライセンスの付与をしていただくことができました。ちなみにライセンスはMITライセンスです。

https://github.com/yulrizka/redmine2github

このツールでできないこと

以下のことはredmine2githubでは実現できません。しかし、今回の要件では作業履歴等が参照できれば良いという程度の要件だったためこのツールを利用することにしました。

  • ファイルの移行
  • Redmine上の他のチケットへのリンクの参照をGitHub issueに合わせて変換を行う

2. RedmineとGithubの設定をする

redmine2githubを利用するために、Redmine及びGitHubの設定を行う必要があります。

Redmine

  • 管理 > 認証のページから「RESTによるWebサービスを有効にする」

管理画面の承認ページから「RESTによるWebサービスを有効にする」を有効にします。

f:id:vasilyjp:20190215102255p:plain

  • 個人設定のAPIアクセスキーをメモ

個人設定の画面からAPIアクセスキーをメモします。これはredmine2github実行時に指定する必要があります。

f:id:vasilyjp:20190215102312p:plain

  • csvをダウンロード

チケット一覧のページで必要なチケットだけ絞り込んだら、以下の画面右下のCSVを選択しチケットの一覧をダウンロードします。

f:id:vasilyjp:20190215102324p:plain

github

GitHub側の設定はissueを作成できる権限のあるユーザーの作成が必要です。 各個人のユーザーでもかいまいませんが、そのユーザーとしてissueが作成されます。

3. 移行ツールをcloneして自分好みにカスタマイズ

Redmineを日本語で利用している場合は、以下のようにカスタマイズしないとカテゴリや題名などを取得できません。

https://github.com/katsuyan-stt/redmine2github/pull/2/files#diff-222c979e694bb42a7f8468f67bbaddf0R111

redmine2githubは1ファイルのRubyスクリプトでできており、簡単にカスタマイズが可能です。自分のRedmine環境に合わせてカスタマイズして利用してください。

4. 実行

最後にredmine2github.rbを実行します。 オプションはredmine2githubのREADMEを参照してください。

 ruby redmine2github.rb リポジトリのユーザ/組織 リポジトリ名 ダウンロードしたCSVのパス -e 上の3でメモしたAPIキー,RedmineのURL -m -s -v -c 完了

移行したことによる意外なメリット

ツールの移行によってEC2の料金や管理コストが減ることなどは考えていましたが、他にも以下のようなメリットがありました。

  • コードや問題の把握が加速した
  • コードレビューがより活発になった
  • CIツールの移行によってプロジェクトの理解が深まった

コード管理やチケット管理を普段使い慣れているGitHubに移行したことにより、コードや問題の把握が加速しました。また、GitHubにコード管理が統一されたことによってコードレビューがより活発になりました。そして一番のメリットは、CIツールの移行によりプロジェクトの理解が深まったことです。CIツールの移行にはプロジェクトのビルドだけでなく、アプリケーションにもある程度詳しくなる必要があります。これにより、プロジェクトの理解を深めることができました。

今後の展望

以上のようにツール移行によりさまざまなメリットがありました。しかし、まだまだ改善の余地があり将来的には以下の実現を考えています。

  • CircleCIを利用した継続的デプロイメント(CD)の実現

今回CIをJenkinsからCircleCIに移行しましたが、Jenkinsでやっていたことを移行したのみで継続的デプロイメントの実現はできませんでした。もともとCDをしようという動きがないプロジェクトだったため簡単には実現できません。しかし、よりリリースのサイクルを早めユーザーに価値を提供できるようCDの実現を試みています。

まとめ

本記事ではEC2で管理していたツールをSaaSへ移行したことについて紹介しました。RedmineからGitHub issueへの移行など少しニッチな内容も含まれていましたが、少しでもお役に立てれば幸いです。

ツールの改善は直接ユーザーに価値を提供することはできません。しかし、チームや会社全体の作業効率やサービスの品質を高めるなど間接的にユーザーへの価値提供を加速させることができます。弊社では、チームや会社全体の業務の仕組み化を自ら進んで考えることができるエンジニアを募集しています。興味がある方は以下のリンクからぜひご応募ください。

www.wantedly.com

カテゴリー