シンクロ・フード エンジニアブログ

飲食店ドットコムを運営する、株式会社シンクロ・フードの技術ブログです

複雑化していた Java の環境構築を Dev Container 化して Eclipse から移行しました

こんにちは、アプリケーション基盤チームの坂本です。
今回は Java の開発環境を Dev Container 化した対応について紹介します。

弊社の開発環境について

弊社では「飲食店ドットコム」をはじめとした飲食店向けのサービスを多数運用しています。
それらのサービスは元々全て Java で書かれていましたが、数年前から段階的に Rails への移植を行っています。
主要なサービスのいくつかはすでに移植が完了していますが、一部のサービスはまだ移植の途中であり、Java で実装された機能と Rails で実装された機能が混在しています。
また、社内システムの中には Rails 移植の工数をかけない判断が下されたものもあります。

このような状況のため、開発者は Java と Rails の両方の環境構築をして業務を行っています。
Rails の環境構築は docker compose を使って比較的簡潔な手順で構築できます。
一方で、Java の環境構築は以下のような問題が発生していました。

  • ローカルに Java、Eclipse、Tomcat などを入れて、手順書を見ながら手動で様々な設定をする必要がある
  • 環境構築手順がいくつも条件分岐していたり、複数の記事に分かれていたりと複雑化している
  • 依存関係解決のため、CodeArtifact の token を取得して Eclipse に渡す手順があり、手順書では毎日手動でやることになっていた
  • バッチを実行するために専用の手順書通りに設定を行い、切り替える必要がある
    • この切り替えが面倒なため、同じリポジトリを2つクローンして片方を Web アプリ用、もう一方をバッチ用にして凌いでいる人が複数人いた

そのため、新しくインターンや新卒の方が入った際には環境構築に詰まって社員に質問することが頻繁に発生したり、環境構築に2日以上かかってしまう場合があるなど、何かしら改善したい状況でした。

今回の対応の概要

前述のような問題があったものの、以下のような理由から対応されないままになっていました。

  • 複数のチームにまたがって利用する環境のため、対応するチームが明確に決まっていない
  • 一度構築すれば当分は再度構築する必要がないこと
  • 弊社の人員増加は緩やかなためオンボーディングを改善する優先度がそれほど高くないこと
  • 私を含め最近入ってきたメンバーは Java の環境や歴史的経緯に詳しくなく、触りにくさを感じていたこと

そこで、今回の対応では弊社の価値創造と呼ばれる制度を利用して Java の開発環境の Dev Container 化を行っています。
この制度では、毎月業務時間の10%以下の時間であれば、保守性が気になっている部分をリファクタリングしたり、業務改善用のアプリケーションを作成したりすることができます。

今回の対応では以下の点を重視しつつ Java の開発環境を Dev Container 化しました

  • Dev Container で簡単に構築できるようにする
  • Eclipse で構築した環境とゆるく共存でき、段階的に移行できるようにする

今回の対応のポイント

ここからは今回の対応のポイントをいくつかピックアップして紹介します。

Tomcat

弊社では、Java で書かれた画面と Rails で書かれた画面が混在しているアプリケーションがあるため、開発環境では全てのアクセスが一旦 Apache を通り、そこで URL のパスなどから Rails と Tomcat に振り分けられます。
Tomcat では複数の Java のアプリケーションが動いていて、設定に応じてさらに振り分けられます。
そのため、各アプリケーションの Dev Container 環境に Tomcat を入れて違う port で動かす場合、Apache の設定も変更しなければいけません。
また、その際に既存の方法で構築した環境も壊れないようにする必要もあります。
さらに、構築手順の完全移行後に余計な設定を消す対応も必要になります。
そこで、今回の対応では1つの Tomcat で複数の Java アプリケーションが動く現状の構造をそのままにすることで、Apache は何も変更せずに対応することにしました。

これを実現するため、Tomcat のリポジトリを用意しました。
利用者はこれを clone して docker compose up -d することで Tomcat が起動します。設定などは不要です。
起動しておけば各アプリケーションの Dev Container 環境でビルドされた成果物が volume 経由で自動的に配置される仕組みです。
これによって、一度起動したら特に意識することもなく、今まで通り開発環境の URL にアクセスすれば Apache 経由で Dev Container 環境でビルドしたアプリケーションにつながります。

Tomcat のリポジトリは compose.yml や tomcat の設定ファイルなどを含んでいます。
compose.yml はおおまかに以下のようになっています

services:
  shared-tomcat:
    build: .
    container_name: shared-tomcat
    command: ["/opt/tomcat/bin/catalina.sh", "run"]
    volumes:
      ...
    ports:
      - "8080:8080" # HTTP
      ...
    networks:
      - hoge
    environment:
      - CATALINA_OPTS=...

volumes:
  # Java のビルド結果を格納するボリューム
  # Dev Container と共有するためnameをつける(つけないとvolume名にprefixが付く)
  java_build_target_hoge_app:
    name: java_build_target_hoge_app
  java_build_target_huga_app:
    name: java_build_target_huga_app
  ...

# DB などのコンテナがあるネットワークに接続。DB などはRails環境と共通
networks:
  hoge:
    external: true

Apache 側を変えなくて良いように port を被せているので、Eclipse 側で使っている Tomcat は停止する必要があります。
ただし、新しい環境構築方法で何か問題が発生した場合には単に Dev Container 用の Tomcat を停止して、Eclipse 側で使っている Tomcat を起動すれば元の環境に戻せます。
これにより、問題が発生しても旧環境に戻して開発を続行できるため低リスクで移行できます。
同時起動ではないので完全な共存と書くと誤解を招きそうですが、Tomcat を起動したり止めたりするだけで切り替えできるので"ゆるく共存"と書いています。

アプリケーション側の構築

アプリケーション側では、基本的にReopen in Containerをするだけで Dev Container 環境がビルド・起動され、環境構築が完了します。

アプリケーション側の Eclipse 側環境との干渉防止

アプリケーション側でも、Eclipse で構築した環境などと干渉しないための対策をいくつか行っています。

まず、Maven の pom のプロファイルを分けています。
いくつかのプラグインの設定を変更したりしたいのですが、そのままだと Eclipse で構築した環境と干渉してしまいます。
そこで、Dev Container 環境では環境変数を設定し、その環境変数がある場合はプロファイルを切り替えるようにしています。
これによって Dev Container 環境以外に影響を及ぼさずに依存解決やプラグイン・ビルドに関する設定を変更することができます。

<profile>
  <id>devcontainer</id>
  <activation>
    <property>
      <!-- DEVCONTAINER という環境変数が true の場合に有効になる -->
      <name>env.DEVCONTAINER</name>
      <value>true</value>
    </property>
  </activation>
  <build>
    <plugins>
      <plugin>
      ...

バッチのビルドに使うプロファイルも同様に他の環境と分けています。
バッチのビルドでは、Eclipse が自動的に解決してくれていた依存関係がいくつかあるため、このプロファイルでのみそれらを CodeArtifact から取得するように変更しています。

また、Dev Container 専用の JNDI 設定を記述したファイルを用意し、Dev Container 環境ではそちらを読み込むようにすることで、環境による差異を吸収しています。

アプリケーション側の便利機能

構築される環境には以下のような特徴があります。

CodeArtifact の token 自動取得

Dev Container 環境起動時に自動的にホスト側で CodeArtifact のトークンが取得され、コンテナの環境変数に渡されます。
キーチェーンのパスワード入力と MFA コードの入力は必要ですが、これは手動対応でも必要だったのでそのままにしています。

バッチ実行の簡易化

Dev Container 環境では、VS Code のタスクを実行するだけで簡単にバッチをビルドしたり実行したりできるようにしています。
VS Code のタスクでは以下のように args を指定することで引数を指定できるので、それをバッチ実行用のシェルスクリプトに渡しています。
これによって専用手順での切り替えは必要なくなり、同じリポジトリを2つクローンするなどの回避策も不要になりました。

// バッチ実行タスク(引数指定可能)
{
  "label": "Execute Batch",
  "type": "shell",
  "args": [
    "${input:taskClassName}",
    "${input:taskArgs}"
  ],
  ...
}

// 入力変数の定義
"inputs": [
  {
    "id": "taskClassName",
    "type": "promptString",
    "description": "実行するタスククラスの完全修飾名を入力してください (例: synchrofood.hoge.HogeTask)"
  },
  {
    "id": "taskArgs",
    "type": "promptString",
    "description": "タスクの引数を入力してください。なければ空欄のままEnterしてください",
    "default": ""
  }
]

デバッガーの利用

デバッガーについても VS Code 標準の Run And Debug を押すだけで対応可能です。
Tomcat に接続されてブレークポイントなど自由に使用できます。

ログの閲覧

ログについても Dev Container 環境起動時に自動的に Tomcat にあるログが自動で tail コマンド によって表示されるようになっています。
間違ってそのターミナルを閉じてしまった場合などは VS Code のタスクでいつでも開くことができます。
旧環境では全てのアプリケーションのログが混ざっていましたが、新環境ではそのアプリケーションに関連するログだけ閲覧できます。

導入後の経過

導入後1ヶ月ほど経っていますが、今のところ特に問題の報告はないようです。
逆に「このアプリケーションにも導入してほしい」といった要望はあったため対象を拡大中です。
このまま問題がなさそうであれば、環境構築手順書を更新し、正式な手順にしたいと思っています。

まとめ

今回の対応によって、複雑化していた Java の環境構築が Dev Container の起動と Tomcat コンテナの起動のみで完結するようになりました。
これにより、Javaの環境構築にかかる時間が劇的に短縮され、手順も簡素になりました。
また、ローカル環境に Java や Eclipse、Tomcat などを入れる必要がなくなり、手動での設定もほぼ不要になりました。
毎回手動で行っていたトークン取得が自動化されたり、バッチ実行が簡単になり切り替えも不要になるなどの嬉しい変化もありました。