いしぐめも

プログラミングとかしたことを書きます。

VSCode の DevContainer で JDK 1.8 の開発環境を作る

自分が開発に参加しているOSS「Personium」では、adoptopenjdk8 を使用して開発を行っています。正直、Java歴が浅く、JDKのバージョンとかよくわかっていないんですが、OpenJDK 1.8の開発環境です。

devcontainer使用に至ったわけ

「devconteiner」は、vscode上でコンテナに乗り込んで開発を行える便利機能なんですが、docker-composeにも対応しており、コンテナで開発環境一式を揃えるといった使い方もできます。

code.visualstudio.com

Personiumもそんな複数コンテナを使用した開発を実施しています。ちょっと経緯を書きます。

Personiumの構成

Personiumは主に3つのサーバーで構成されます。

  • APサーバー(personium-core + personium-engine)
  • DBサーバー
  • ReverseProxy

DBはElasticSearch, ReverseProxyはnginxでほぼ固定になっているので新規に開発するということはないのですが、Tomcat上に乗っかるcoreとengineはPersoniumのキモなので重点的に開発するのはココです。

personium-coreでは認証、OData, WebDAVといったユーザーデータの取り扱いを主に司りますが、personium-engineではユーザーのデータ領域内でユーザー定義スクリプト実行を行います。それぞれ別のアプリケーションとして開発されていて、Tomcat上で動いているengineに対して、coreが内部的にHTTPでリクエストして連携する方式を取ります。

従来のテスト方式

personium-engineの開発では、単体テストの他に、coreとの連携を行うシステムテストのようなものも行います。従来の方式では、Tomcatとnginx, elasticsearchを使用して本番と同じ(engine以外)環境を作り、Grizzlyを使用して開発中のengineを動作、リバプロへのリクエストを通してengineが動作するかを見ていました。

毎回テストを行うたびに環境が汚れるのが気に食わないので、他のプロセスをDockerコンテナにもっていき、テスト時はホスト側で動作している Grizzly(engine) にリクエストがいくように設定していました。WindowsMac の Docker Desktop では host.docker.internal というホストが使用可能で、ホストのポートに通信をすることができるんですが、Linux ではこれが使えません。 そこで以下のように設定することで host.docker.internal を使用していました。

version: "3.7"

services:
  personium-core:
    build:
      context: ./dockerfiles/personium-core
    extra_hosts:
      - "host.docker.internal:host-gateway"

こんな感じで docker-compose.yml に host.docker.internal を追加することで、

# engine configurations
io.personium.core.engine.host=host.docker.internal
io.personium.core.engine.port=9998
io.personium.core.engine.path=

コンテナ内からホストへの通信を行うことができます。

host.docker.internal 疲れ

普段はLinuxで開発していたのでそこらへんは気にしていなかったんですが、Windows + WSL2 で開発しようと思い立ち、やってみたところ host.docker.internal で逆にドハマりしました。Windows側に Java の開発環境を導入するなんてつらそうだしやりたくなかったので、WSL2上の Ubuntu で開発を試みたんですが、なんと host.docker.internal では Ubuntu側のプロセスに到達できなかったのです。

Ubuntu のIP直打ちなら到達できるのですが、どうしても host.docker.internal で到達できるようにする方法が見つからなかったので devcontainer の使用に思い至ったわけです。

構成

というわけで、今回はdevcontainerで docker-compose を使用した開発に入門してみようと思います。

.devcontainerフォルダの準備

vscodeで「devcontainer」を使用するには .devcontainer フォルダを作成します。

f:id:yoh1496:20210724225606p:plain
devcontainer

その中に devcontainer.json という設定ファイルを作成し、下記のように環境設定を記載しました。

{
    "name": "Personium Engine Development Environment",
    "dockerComposeFile": [
        "docker-compose.yml",
        "docker-compose.devcontainer.yml"
    ],
    "service": "personium-engine-dev",
    "workspaceFolder": "/workspace/personium-engine",
    "shutdownAction": "stopCompose",
    "extensions": [
        "vscjava.vscode-java-pack"
    ],
}

dockerComposeFile には使用する docker-compose のymlファイル。service にはymlファイルに記載した、開発時に接続するコンテナを指定します。そしてworkspaceFolder には、コンテナに接続した際に開くフォルダを指定します。Javaの開発用コンテナなので、vscjava.vscode-java-pack のエクステンションを有効にしておくと便利です。

dockerComposeFile に2つのdocker-composeファイルを指定しているのは、devcontainer用に使用するときとそうでないときに設定を変えたい場合に有効です。たとえば、例のように docker-compose.devcontainer.yml を用意して、下記のようにvscodeのdevcontainerとして使用する場合のみに有効にしたい設定を記述する感じです。

docker-compose.yml

version: "3.7"

services:
  elasticsearch:
    image: elasticsearch:6.8.12
    # ほかの設定

  activemq:
    image: rmohr/activemq:5.15.9
    # ほかの設定

  memcached-lock:
    image: memcached:1.6.7
    # ほかの設定

  memcached-cache:
    image: memcached:1.6.7
    # ほかの設定

  personium-core:
    build:
      context: ./dockerfiles/personium-core
    # ほかの設定

  personium-engine-dev:
    image: ghcr.io/yoh1496/adoptopenjdk-8-vscode:latest
    volumes:
      - personium_data:/personium_nfs

  nginx:
    build:
      context: ./dockerfiles/nginx
    # ほかの設定

networks:
  default:

volumes:
  personium_data:
    driver: local

docker-compose.devcontainer.yml

version: "3.7"

services:
  # マージされる↓
  personium-engine-dev:
    volumes:
      # /workspaceにマウントされるフォルダ
      - ../../:/workspace:cached
      # devcontainerでのみ使用するボリューム設定
      - ./extensions/:/personium/personium-engine/extensions/:ro
    # すぐにコンテナが終了しないようにcommandの上書き
    command: /bin/sh -c "while sleep 1000; do :; done"

ここまで準備すれば、左下のリモートウィンドウを開くボタンから「Reopen in container」を選ぶことで、docker-composeからコンテナ一式が立ち上がり指定したコンテナに繋ぐことができます。

f:id:yoh1496:20210724231354p:plain
start devcontainer

Dockerfileの準備

と、ここまで書いてDockerfileについて書くのを忘れていました(どちらかというと今回の記事のキモはここから)。

Javaの開発環境なので、vscode-java-pack エクステンションを有効にしていますが、その1つの「Language Support for Java(TM) by Red Hat」は、動作要件として Java 11以降が導入されている必要があります 。ですので、今回は Ubuntu20.04 のコンテナをベースに開発環境を揃えました。

FROM ubuntu:20.04

RUN apt update && apt install -y wget apt-transport-https gnupg git

# https://adoptopenjdk.net/installation.html
RUN wget https://adoptopenjdk.jfrog.io/adoptopenjdk/api/gpg/key/public && \
    gpg --no-default-keyring --keyring ./adoptopenjdk-keyring.gpg --import public &&  \
    gpg --no-default-keyring --keyring ./adoptopenjdk-keyring.gpg --export --output adoptopenjdk-archive-keyring.gpg && \
    rm adoptopenjdk-keyring.gpg && \
    mv adoptopenjdk-archive-keyring.gpg /usr/share/keyrings && \
    echo "deb [signed-by=/usr/share/keyrings/adoptopenjdk-archive-keyring.gpg] https://adoptopenjdk.jfrog.io/adoptopenjdk/deb focal main" | tee /etc/apt/sources.list.d/adoptopenjdk.list && \
    apt update

# install jdk and maven
RUN apt install -y adoptopenjdk-11-hotspot adoptopenjdk-8-hotspot maven

ENV JAVA_HOME=/usr/lib/jvm/adoptopenjdk-8-hotspot-amd64

このようにすることで、adoptopenjdk の 11と8 が導入されたUbuntuを使用することができます。参考したのは以下のページです。

adoptopenjdk.net

起動したコンテナの管理

vscodeで起動したコンテナの管理は以下のようにDockerのタブから行えます。

f:id:yoh1496:20210724234313p:plain
コンテナの管理

devcontainerでこう変わった!

使ったからにはどうなったかというのを書いてみたいと思います。

よくなった点

  • ホストが介在しなくなったので、host.docker.internal に悩まずに済むようになった
  • もはやホストのどこにも(WSL2にすらも)Javaの開発環境を入れなくてよくなった
  • 開発環境のセットアップをしやすくなった

悪くなった点

  • 動作が遅くなった(ターミナルが遅いのかも?)
  • vscodeからコンテナを管理するのが面倒

これから考えたいこと

確かにdevcontainerは便利なんですが、プロジェクトに取り込むにあたっては悩みポイントがあります。

リポジトリに入れていいのか問題

まずはコレです。最近になってvscodeがイイ感じな風潮になってきましたが、JavaIDEとしては今一つな出来であると評価してる人もいるので、vscode用のディレクトリ「.devcontainer」を導入してもよいのか悩ましいです。

どこかにリンクを用意しておいて、vscodeを使うときはこれ使うと便利ですよ的なやつでもいいかなと思いますが。

あとは、GitHub Actionsなどでうまい感じに「.devcontainer」の中身を使用した自動テストを書ければ、単に「流行のエディタのためのフォルダ」ではなくなるので、それなりに理由が付けられそうですが、、、

②キャッシュどうするか問題

mavenのキャッシュをどうするか問題があります。まぁこの辺は「決め」の問題なので決めればハイ終わり、なんですが…

適当に docker-compose内に開発用のボリュームでも定義してマウントするかな…

終わりに

JDK 1.8の開発環境をdevcontainerで構築する、という内容を記事にしてみました。どなたかの参考になれば幸いです。

この内容はPersoniumというOSSコミュニティ向けに色々試行錯誤した結果なのですが、まだ「手元で試してみた」程度の内容なので、近いうちにコミュニティで提案してみたいと思います。

最後になりますが、Personiumではコミュニティメンバーを募集しています。もし「Javaなら俺に任せろ」「PDSちょっと興味ある」といった人がいたらぜひお声がけください。