Tips & Tricks

IntelliJ IDEA リモート開発環境の自動構築を試してみよう

こんにちは、JetBrains堀岡です。

IntelliJ ベースの IDE 2021.3 からベータ版として提供されている JetBrains Gateway を用いたリモート開発機能をお試しいただけましたか?

目次

はじめに:2つのワークフロー

WSL2 + Docker 環境における IntelliJ リモート開発環境の構築

Terraform +  AWS EC2 環境における IntelliJ リモート開発環境の構築

はじめに:2つのワークフロー

既に試された方は、JetBrains Gateway 製品ページ やブログポスト「JetBrains Gateway を用いたリモート開発」で紹介されている JetBrains Gateway を用いてリモート開発環境に IDE をインストールする方法をご覧いただき、使ってみたという方が多いかもしれません。

上記の使用方法は IntelliJ IDEA ドキュメント「リモート開発の概要」 によると「クライアントからサーバーへのフロー 」と呼ばれるものです。これは JetBrains Gateway を用いて、既存のリモート開発環境に JetBrains Gateway から JetBrains IDE をアップロード&追加インストールし、利用するものです。

本稿では、もう1つのワークフローである「サーバーからクライアントへのワークフロー」の例を紹介します。このワークフローは、標準化された開発環境の自動構築や、CIの成果物などとして生成される、IDEも含めた使い捨て可能な開発環境の自動構築を念頭に入れたもののようです。この方式のメリットは 開発環境のセットアップを(ほぼ)事前に作り込めるので、JetBrains Gateway で接続してから使い始めるまでの時間を削減できる点です。

利用の流れとしては以下のようになります。

リモートマシン側

  • 環境構築
    • 開発環境(OSやコンパイラ等)セットアップ
    • IDE インストール
    • プロジェクト(ソースコード)セットアップ
  • ソースコードが含まれるディレクトリに対して IDE のサーバー機能を起動
    • 実行コマンド(例:remote-dev-server.sh run <path/to/project> –ssh-link-host remote.server.com)実行すると、標準出力の中にクライアントからの接続に必要となURLが表示される

クライアント側(JetBrains Gateway)

  • 上記で得たURLを用いてリモートマシン側に接続

接続方法が原始的な感じもしますが、クライアントとリモートマシン側の接続方式は、環境によって色々なケースが考えられるので、ユーザーによる作り込みを想定しているか、今後いろいろ改善があるのかもしれません。

今回は例として以下の2つのケースを紹介します。

  1. Windows WSL2 の Docker 環境
  2. Terraform +  AWS EC2 環境

現在の制限事項や既知の問題(2022/2/8現在):

その他、補足・注意事項:

  • JetBrains Gateway およびリモート開発機能はベータ版です。前述の機能制限や既知の問題がある他、皆様のフィードバックを元に様々な機能が追加たリリースが継続的に行われることにより、本稿の内容と最新版では振る舞いが異なる可能性があります。
  • 本稿の内容はリモート開発機能のデモおよび動作確認ためのものです。本番環境での利用は自己責任でお願いします。

WSL2 + Docker 環境における IntelliJ リモート開発環境の構築

WSL2 + Docker のローカル環境でリモート開発機能の構築例を紹介します。

クライアント(Windows)から WSL/Docker 上の IntelliJ IDEA には SSH を経由せず直接接続する例です。YouTrack 等で JetBrains のリモート開発チームが紹介している以下の例をベースに試してみましょう。

テスト環境

  • HW: Intel(R) Core(TM) i7-10710U、32GB Memory
  • OS: Windows 11 Pro 21H2
  • WSL:
    • WSL バージョン: 0.51.2.0
    • カーネル バージョン: 5.10.81.1
    • WSLg バージョン: 1.0.30
    • Windows バージョン: 10.0.22000.466
  • JetBrains 製品

環境構築手順

IntelliJ IDEA で空のプロジェクトを WSL 上に作成します。

以下の Dockerfile を追加します。

FROM openjdk:11-jdk-slim
RUN apt-get update && apt-get install -y git curl unzip procps time
ARG USERNAME=intellij
ARG GROUPNAME=intellij
ARG UID=1000
ARG GID=1000
ARG PROJDIR=project
ARG IDEURL=https://download.jetbrains.com/idea/ideaIU-2021.3.2.tar.gz

#Create non-root user
RUN groupadd -g $GID $GROUPNAME && \
   useradd -m -s /bin/bash -u $UID -g $GID $USERNAME
USER $USERNAME
WORKDIR /home/$USERNAME

#Clone project code and build (to cache dependencies,etc)
RUN git clone https://github.com/spring-projects/spring-petclinic $PROJDIR
RUN cd $PROJDIR && ./mvnw -ntp package

#Download & Install IntelliJ IDEA
RUN curl -fsSL -o ide.tar.gz $IDEURL && \
mkdir ide && \
tar xfz ide.tar.gz --strip-components=1 -C ide && \
rm ide.tar.gz

#Install plugin
RUN time ide/bin/remote-dev-server.sh installPlugins $PROJDIR com.intellij.ja

#Following features are disabled
#RUN time ide/bin/remote-dev-server.sh warm-up $PROJDIR
#ENV REMOTE_DEV_SERVER_JCEF_ENABLED=1

#Default command
CMD ide/bin/remote-dev-server.sh run $PROJDIR --listenOn 0.0.0.0 --port 5993

補足:プラグインのインストール

上記例では日本語言語パックをインストールしています。IDE リモートサーバー環境に対する、コマンドラインによるプラグインのインストールを行うために remote-dev-server.sh installPlugins コマンドを使用しています。このコマンドを実行するにはリモートサーバー環境がインターネット接続されている必要があります。また、引数として指定する Plugin ID (上記例の場合、日本語言語パック com.intellij.ja) は、JetBrains Marketplace のプラグインの version ページ等で確認する必要があります。

次に、IDE 内のターミナルを開き、

docker build -t remote-dev .

を実行します。今回の例(および上記参考)では、ビルド済みの環境を準備したかったので docker build の中で、mvn コマンドを実行しています。そのため 10-15分くらい時間がかかります。

docker build が完了したら、イメージを実行します。リモートサーバーの接続に使用する 5993 と Spring Boot アプリのポート番号 8080 がそのままアクセスできるよう -p に指定します。

また、Dockerfile の中では、(参考にした例に合わせて)最後に CMD で remote-dev-server.sh (IDEリモートサーバーの起動コマンド)を指定していますが、環境を残したままリモートサーバーを再起動したい等もあるかもしれないので、 /bin/bash を指定してシェルが起動するように、実行します。

docker run -i -t --rm --name my-remote-dev -p 5993:5993 -p 8080:8080 remote-dev /bin/bash

イメージ起動後、コンテナのシェルの中で、remote-dev-server.sh を実行します。

ide/bin/remote-dev-server.sh run project --listenOn 0.0.0.0 --port 5993

起動確認メッセージが表示されるので、ENTER キーを押し、リモートサーバーの起動を継続します。

リモートサーバーが起動されると、標準出力の中に、サーバーに接続するためのリンクが出力されます。ぼんやりしていると流れていってしまうので、ご注意ください。

tcp:// … のリンクを(ダブルクリックして)選択してコピーします。

(ToolboxApp等から)JetBrains Gateway を起動します。

Connect with a Link の箇所に、コピーしたリンクを貼り付け、「接続」ボタンをクリックします。

無事接続できると、以下のようにプロジェクトが開きます。

Spring Boot アプリを実行してみましょう。

どこでも検索(Shift x 2)で main  で main 関数を検索し、PetClinicApplicaiton.java を開きます。

JDK が未設定であるため、赤線エラーが表示されます。ここで、エディタ画面右上に表示されている「SDK のセットアップ」をクリックし、インストール済みのJDK を選択します。すると、JDK 用のインデックス処理が実行され、完了すると赤線が消えます。

ここで ▶︎ のガターアイコンをクリックしPetClinicApplication を実行します。 

起動したら、http://localhost:8080 にアクセスしてみましょう。無事表示されればセットアップ成功です。

トラブルシューティング

実行時にコンパイルエラーになる場合、Maven ツールウィンドウから clean や compile が実行できることを確認してみてください。ここでエラーが起きなければ、JetBrains Client 側で準備完了のように見えているが、バックエンド側では処理中であることがあるようです。この場合、しばらくたってから、実行やデバッグをお試しください。

localhost で WSL2 環境へのアクセスがうまくいかない場合は、WSL 上で “hostname -I”  を実行し、localhostの代わりに WSL2 ホストの IP アドレスを指定すると解決することがあります。

リソースの使用状況

今回のテスト環境では WSL2 の .wslconfig ファイルで メモリ8G、CPU 4を指定して試していますが、タスクマネージャーで VmmemWSL を眺めてみると、CPU、メモリ共にフル活用しているようです。リモートサーバー機能により必要な処理が軽くなるわけではないので、快適に実行するには、ローカル環境同様、良いスペックのPCとWSL環境へのリソース割り当てが必要でしょう。

IntelliJ リモートサーバーのログ

コンテナ内でサーバーとして実行している IntelliJ IDEA のログ(idea.log) は以下の箇所で確認することができます。

<リモートサーバーのユーザーのホームディレクトリ>/.cache/JetBrains/RemoteDev-IU/_home_intellij_project/log 

IntelliJ IDEA の「サービス」ツールウィンドウで Docker に接続し、実行中のコンテナにアクセスすると以下のように確認することができます。

.vmoptions ファイルによる最大ヒープ使用量の調整

(IDE のインストールディレクトリ)ide/bin/idea64.vmoptions によりデフォルト値として 750 MB と指定されていますが、以下のスクリプトで 2048MB に変更しています。その結果、現在のリリースでは IDE サーバーの最大ヒープサイズは 2048MB で動作するようになっています。

<ide install dir>/plugins/remote-dev-server/bin/launcher.sh

2048MB 以上またはそれ以外に変更したい場合、idea64.vmoptions を変更すると良いでしょう。

例えば、コンテナ内の<home>/ide/bin ディレクトリで

sed -i -e ‘s/-Xmx750m/-Xmx4000m/’ idea64.vmoptions

を実行し、再度リモートサーバーを起動すると反映されます。ただし、この辺は変更される可能性もあるので注意が必要です。以下のように idea.log で起動時パラメータを確認しましょう。

このサンプルではカバーされていない検討事項

今回の例では、リモートサーバーを起動し、JetBrains Gateway から IDE を開くまでを見ました。

実際に開発に使用する場合

  • git の設定
  • ソースコード等をマウントしたボリュームに置くかどうか

のなどの検討が必要でしょう。

Terraform +  AWS EC2 環境における IntelliJ リモート開発環境の構築

このセクションでは、別の例として Terraform を使って AWS EC2 上に開発環境を自動構築する例を紹介します。IntelliJ IDEA のリモート開発機能は特例のクラウド環境への依存性はないので、Azure や GCP 等他のクラウド環境の構築は可能であると思われます。

テスト環境

  • HW : Mac mini (M1 2020)
  • OS: macOS 12.1
  • JetBrains 製品
    • ローカル環境 (Toolbox Appからインストール)
    • サーバー環境(EC2のcloud-initスクリプトでインストール)
      • IntelliJ IDEA Ultimate: 2021.3.2 + Japanese Language Pack
  • AWS CLI: aws-cli/2.4.7 Python/3.9.9 Darwin/21.2.0 source/arm64 prompt/off
  • Terraform: v1.1.2on darwin_arm64
  • (自由にEC2とか立てられる)AWSアカウント この例では EC2 インスタンスタイプ c5a.xlarge を使用しています。無料枠ではないのでご注意ください。

環境構築手順

AWS CLI/Terraform 環境を準備します。こちらのチュートリアルができる状態になっていればOK

(Terraform を実行するローカル環境に)Terraform で作成する EC2 インスタンスの SSH 接続に使用するキーペアが存在することを確認します(無い場合は

ssh-keygen -t rsa -m PEM

等で生成。以降紹介するサンプルプロジェクトでは EC2 に登録する公開鍵の場所を “~/.ssh/id_rsa.pub” として参照しています)

IntelliJ IDEA でサンプル Terraform プロジェクト https://github.com/mhorioka/hello-ijremotedev-terraform-ec2 を開きます。

README に説明がありますが、各ファイルの説明を日本語ですると、

  • cloud-init.sh
    • EC2 インスタンス作成時に実行される環境構築のための処理を定義(1つめの例のDockerfileとほぼ同じ)
      • コンパイラや JetBrains IDE、プラグイン のインストール
      • ソースコードの準備と必要なライブラリ等をインストールするためビルド処理実行
      • AWS 環境で使うCloudWatchエージェントのセットアップ
  • cloudwatch.tf
    • CloudWatch ダッシュボード(CPU、メモリ、ディスク利用率)とアラームアクション(CPU利用率が低い場合に EC2 インスタンスを停止する)を定義
  • ec2.tf
    •  EC2 インスタンスやセキュリティグループの構成を定義
  • iam.tf
    • CloudWatch 用 IAMロール を定義
  • main.tf
    • Terraform と AWS provider を定義
  • network.tf
    • VPC 定義
  • outputs.tf
    • terraform apply の実行結果として IP アドレスが表示されるようにする
  • variables.tf
    • SSH公開キーのパスや、EC2 インスタンスの種類やAWSのリージョン等変更しそうなものとデフォルト値を定義

必要に応じて(.ssh 公開鍵ファイルや使用したいEC2インスタンス等) variables.tf を変更します。ちなみに、今回このブログポストのために、Terraform を初めて使ったのですが、Terraformプラグインがあると、テンプレートや補完が便利な上、Cmd + B/Ctrl + B で使用箇所にジャンプできたり、名前変更リファクタリングができるので、HCLの確認や編集が捗ると思いました。

次に、IDE 内のターミナルを開き

terraform init
terraform apply

を実行します。

apply を実行すると確認がありますので、

yes

を入力します。

EC2 インスタンスの起動が完了すると、以下のような画面が表示されます。outputs.tf に指定した、EC2 インスタンスの外部IPアドレスが出力結果として表示されます。

EC2 インスタンスが起動されているので、ssh で EC2 インスタンスにログインします。例:

ssh ec2-user@EC2インスタンスの外部IPアドレス

でログインします。

サンプルの cloud-init.sh では、mvn コマンドを packaging まで実行しているので、大体10分くらいかかります。気が早い方は、EC2 インスタンス上で

sudo tail -f /var/log/cloud-init-output.log

で実行ログが見れるので、完了するまで待ちます。以下の画面がでると cloud-init.sh が実行完了です。

リモートサーバー機能の起動コマンドは、手打ちでも問題ないですが、cloud-init.sh の中で環境に合わせて出力するようにしているので、こちらを利用します。

(tail を実行中の場合抜けてから)EC2 に接続したターミナルのコマンドラインに、リモート開発サーバーの起動コマンドを指定して実行します。以下の例では、–ssh-link-host にEC2インスタンスの外部IPアドレスを指定しています(必要に応じて ‘ec2-user’ 等をお使いの環境に合わせてください)。

ide/bin/remote-dev-server.sh run /home/ec2-user/spring-petclinic --ssh-link-host $(curl -q http://169.254.169.254/latest/meta-data/public-ipv4)

ENTER を入力し、しばらくすると、標準出力に、リモートサーバープロジェクトにアクセスするためのリンクが表示されます。ログはどんどん流れていくので、ぼんやりしていると、見逃してしまうので注意が必要です。

IntelliJ IDEA のターミナルでは Http link がハイライトされ、クリック可能になっているので、クリックします(ハイライトされない環境の場合は、URL をコピーして、ブラウザに貼り付けてください)。

ブラウザが開き、JetBrains Gateway を開くかどうか確認されますので、「開く」を選択します。

JetBrains Client でリモートプロジェクトに対して SSH 接続を確認するダイアログが表示されますので、「接続を確認して続行」を選択します。

JetBrains Gateway が(必要時応じてサーバー側に合わせた)JetBrains Client をダウンロードし、JetBrains Client でプロジェクトが開きます。

Docker の例と同様に、Spring Boot アプリを起動してみましょう。どこでも検索(Shift x 2) でSpring Boot アプリの main を探して(初回は検索に少し時間がかかるかもしれません)開きます。

今回はデバッグ実行してみましょう。

デバッグツールウィンドウの 8080 のところをクリックして、「ポート転送してブラウザを開く」を選択すると、EC2 上のウェブアプリを SSH 経由で開くことができます。

ここで、ブレークポイントを設定してみましょう。

エンドポイント ツールウィンドウ等を活用して、/owners/find に対するメソッド(OwnerController.java の initFindForm)にブレークポイントを設定します。

先ほど開いたブラウザで Find Owner ページにアクセスしてみましょう。

ブレークポイントで止まりました。

リソースの使用状況

使用している EC2 マシンのリソース使用状況は、サンプルスクリプトの中で Cloud Watch で作成した簡易ダッシュボードでみることができます。ご自身の環境ではどのようなEC2 インスタンスタイプを選択するのがよいか検討してみてください。

テスト環境の削除

テストが終わったら 

terraform destroy

で AWS リソースの削除を行います。

まとめ

JetBrains IDE によるリモート開発 「サーバーからクライアントへのワークフロー」により、開発環境を自動構築し、JetBrains Gateway からアクセスする例を紹介しました。リモート開発機能の利用の一例として参考になれば幸いです。

「うちの開発環境で使うにはこんな機能が欲しい」等フィードバックやご質問がありましたら、Twitter(@masaruhr) や JetBrains 日本語営業・サポート窓口までぜひお知らせください。