いしぐめも

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

devcontainerを導入したら開発(主にテスト)が楽になった【Visual Studio Code】

Javaで開発しているOSSにおける Visual Studio Code の devcontainer 使用について以前記事を書きましたが、いったん便利に使えるようになってきたので記事にまとめたいと思います。

yoh1496.hatenablog.com

このような方におすすめ

今回の記事はこんな方には役に立つんじゃないかと思います。

  • Visual Studio Code の devcontainer機能について知りたい
  • 他のプログラムと連携するソフトウェア開発での開発環境構築を楽にしたい
  • 他のプログラムと連携するソフトウェア開発でのテストを楽にしたい

前置き:Personiumというソフトウェアについて

「Personiumはパーソナルデータストアのオープンソース実装です…」という前置きは他の記事に譲るとして…Personium開発について触れる上で前提となる内容をここに記載したいと思います。

Personiumは以下のように、複数のバックエンドと連携してPDS機能を提供しています(2021年8月現在、デフォルト構成)。

インテグレーションテストでは、これら他のプログラムとの接続を行い、想定通りに動作するかの確認を行っています。例えば、ユーザーのデータ領域はファイルシステム上に作成されますが、そのメタデータが正しくelasticsearchに保存され、正しく取り出せるか、などです。

devcontainer導入前のテスト方法

devcontainerを導入する前のテストは以下のような構成でテストを行っていました。

f:id:yoh1496:20210816142620p:plain
テスト環境初期状態:全部ホスト環境で動かす

こんな感じです。ローカルにあるソースコードコンパイルし、インテグレーションテストではホスト上のGrizzlyで動作するpersonium-coreに対して、9998ポートを通してAPIを叩く、という感じです。personium-coreはlocalhostの9300ポートをリッスンしているelasticsearchなどと通信を行っていきます。

ポイントとしては、elasticsearchとpersonium-coreが直接ローカルのファイルシステムを触ってしまうので、テストを実行するうちにローカル環境が汚れ放題になってしまうという点です。当然、開発途中のテストを行えばデータの不整合も発生しますし、そのたびに手でデータを消してまっさらな状態に戻す必要があります。(と想像しています。そもそもこの構成で手動テストしたことがない)

まずは依存バックエンドのコンテナ化

上記のようなやり方をしていたので、コアを開発しようと思ってやり方などを調べていたら思った以上に億劫でビックリしました。

開発に使用するサービス類を手で管理するのは現実的じゃありませんし、まずはバックエンドで使用している他のサービスをdockerでコンテナ化することはできないだろうか?と考えました。そしてこんな構成になりました。

f:id:yoh1496:20210816164144p:plain
テスト環境第一段階:使用サービスをコンテナ化

docker-compose.ymlを使用し、localhostのポートをコンテナ内のプロセスがリッスンしているポートにマッピングしてやることで、とりあえずはまぁサービスの実行環境を分けることができました。elasticsearchの使用によってホストの環境が汚されることはなくなりましたし、使わなくなったらコンテナを止めて消してしまえばまっさらな状態に戻ります。

ただ、肝心なpersonium-coreの開発環境自体はローカルだったので、ファイルシステムに書き込まれるユーザーデータはホスト環境に直に書き込まれる状態であり、手動で消す必要がちょくちょくありました。

この点については、開発環境自体をコンテナ化してしまえば、テスト時にいくらファイルシステムに書き込んでもホストにはノーダメなので色々と楽になりそうな予感はあったのですが、 その揮発性から開発環境を作ったり壊したりすることに億劫さを感じてしまい踏み切れずにいました。

devcontainer 導入

devcontainerを導入してからはこんな感じです。

f:id:yoh1496:20210816165551p:plain
テスト環境第二段階:開発環境もコンテナに

ローカルにあった開発環境もコンテナ化してみました。こうすることで、ホストにはJDKも入れる必要がなくなり、vscodeとdockerが使える環境があればすぐにでも開発を始められるようになりました。

ソースコードについては、当初はローカルのファイルシステムを開発環境コンテナ上にマウントしていたんですが、マウントのオーバーヘッドやファイル作成時のパーミッションなどが気になったのでボリュームを作成してマウントするようにしました。(乗り込んでからgit cloneするイメージ)。

開発環境コンテナ化で発生した問題

ここでは開発・テスト環境をコンテナ化するにあたって発生した問題・工夫した点を書きたいと思います。

localhost向き先問題

開発環境=ホストマシンな状態であれば、他のソフトウェアをコンテナで動かしていようがlocalhostで通信できたんですが、開発環境をコンテナに移してからは localhostではなくソフトが動作するコンテナを直接指定する必要 が出てきてしまいました。

「そんなんテスト設定内の参照先変えればいいだけじゃん」という話なんですが、Jenkinsで行ってる自動ビルド環境は旧来のlocalhostでelasticsearchが動作しているような気がするので(確かめてない)、おいそれと書き換えることはできませんし、実施する環境に応じて設定ファイル書き換えるのも筋が違う気がします。

なので、結構苦し紛れですが、開発環境コンテナ内で socat を動かし、 localhost:9300をelasticsearch:9300にリダイレクトする」 みたいな脳筋的発想で解決することにしました。

f:id:yoh1496:20210816214250p:plain
socatでlocalhostへの通信を他コンテナに

具体的には、前回記事でも示した通り、開発コンテナが終了しないように無限ループしているところをsocatによる待ち受けに変更しました。

services:yml
  personium-core-dev:
    # Overrides default command so things don't shut down after the process ends.
    # For testing, remapping port with socat
    command: >-
      bash -c '
      socat TCP-LISTEN:61616,reuseaddr,fork TCP:activemq:61616
      | socat TCP-LISTEN:9300,reuseaddr,fork TCP:elasticsearch:9300
      | socat TCP-LISTEN:9200,reuseaddr,fork TCP:elasticsearch:9200
      | socat TCP-LISTEN:11211,reuseaddr,fork TCP:memcached-lock:11211
      | socat TCP-LISTEN:11212,reuseaddr,fork TCP:memcached-cache:11211
      | socat TCP-LISTEN:18888,reuseaddr,fork TCP:personium-engine:8080
      '

こうすることでコンテナ内で叩いた localhost:9300 などが elasticsearch:9300 に転送されて、今まで通り elasticsearch の接続先に localhost を指定していたとしてもちゃんと動くというわけです。

devcontainerの導入によるメリット・デメリット

ここでは、devcontainerを導入することで感じたメリット・デメリットを書きたいと思います。

メリット:簡単に開発環境を準備することができるようになった

まずはコレです。Personiumって開発環境の構築に以下のようなドキュメントを用意していますが、これが VSCodeとdockerがある環境でこのdevcontainer使ってね」 で済むのは大きいと思います。

開発用環境の構築手順 · Personium

わかる人はdevcontainerの設定を読めば、どういった環境を用意すればいいのかわかりますし、開発環境構築に不慣れな人も簡単に導入できます。

あとは独立したコンテナで立ち上がるので、色々と混ざる心配がないです。他のソフトウェア開発で使用しているのとバッティングする場合など、下手に設定を上書きしてしまって「何もしてないのに壊れた!」って状況を回避することができます。

メリット:いろんなバージョンを手軽に試せる

前述のメリットでも書いた通り、開発環境を簡単に構築できるようになったので、いろんなバージョンでの開発を試すことができます。バージョンを変えたとしてもコンテナを落としてビルドしなおすだけです。

また、今回の構成であれば、

メリット:手元マシンのスペックがいらない

SSH経由などでリモートのdocker環境を使えるようにすれば、手元のマシンスペックはVSCodeのフロントエンドを動かすリソースだけで済みそうです。Remote SSHで済むといっちゃ済む話なんですがw

デメリット:docker、gitの知識が必須

コンテナ環境を多用するので、トラブった場合に臆せずコンテナを落としたりボリュームを消したりする必要があります。その場合に、dockerのコマンド叩いたことない、とかそういうレベルだと間違ったボリュームを消してしまったり、苦労することが多そうです。

gitもそうです。ローカルのフォルダをコンテナにバインドする場合は問題ありませんが、コンテナに乗り込んでボリュームにcloneする場合だと、完成したらgitでpushする必要があります。VSCodeのgitプラグインで大抵賄えますので、問題ないかもしれませんが。

終わりに

以上、devcontainerを導入したらいろいろと楽になった、という話でした。

Personiumコミュニティではメンバーを募集中です。コミュニティのSlackなどもありますので、興味を持っていただけたら是非ご参加いただければと思います。

personium.io

今回作成したものは以下のリポジトリに格納してあります(随時更新中

GitHub - yoh1496/personium-core-dev: devcontainer for developing personium-core