【VSCode】コンテナを利用した開発環境の構築について【devcontainer】
Dockerコンテナを使った開発が結構便利で人にもオススメできるレベルになってきたのでご紹介します!正直コンテナって聞いただけで「ウッ」となってしまう方もいると思うので、極力メリットとかも書いてみたいと思います。
Dockerコンテナ×開発環境
そもそも、なんでDockerコンテナを開発環境で使おうという発想に至るのでしょうか?個人的には以下のような「Dockerコンテナが開発環境構築に向いているわけ」があると思います。
Dockerコンテナが開発環境構築に向いているわけ
その①:揮発性・冪等性
コンテナイメージは内部的にレイヤごとに分かれた構成になっていて、commitしない限りはそのコンテナイメージに変更が加わることはありません。つまり、変更をコミットせずにコンテナを落としてしまうと良くも悪くも変更した内容は消えてしまうわけです。本番でDockerコンテナを使用しようとするとこの特性は最初面倒に感じてしまうこともありますが、逆に言えば再現したい状態をコミットしておくことで、新しく作れば毎回同じ状態が再現される というのは試行錯誤するうえではとてもメリットがあるのです。 つまりこれは、ゴチャゴチャになった開発環境をリセットしたい!! という開発環境あるあるを解決できます。
その②:エコシステム
Dockerコンテナを立ち上げるときに使用するイメージはもちろん自分でDockerfileを利用して1から組み立てることもできますが、Dockerが優秀なのはその イメージをベンダーが提供していたり、またそれがレジストリから落とすことができる「エコシステム」 にあると思います。
たいていのミドルウェアはコンテナイメージも簡単に入手できるようになっており、開発環境構築はこのイメージを持ってくるだけで完了してしまうことが多いんじゃないでしょうか。
その③:軽量
上にあげたメリットはうまくやればVMでもディスクイメージを取っておいて使いまわせば達成できなくもないですが、Dockerコンテナの場合はカーネルはホストOSが担うので、軽量ですし起動も早いと言われています。VM立ち上がるの待たなきゃ~~~っていうのがなくなりますね。
また、VMを複数立ち上げると複数のカーネルが立ち上がってしまうのでそこでメモリなりCPUなりリソースを少なからず消費するということも頭に入れておきましょう。別個のカーネルで動いていなければならない要件がなければ、カーネルを複数の環境で使いまわせるコンテナを使った開発環境がリソース的には有利になります。(まぁそんなカツカツな環境で開発するなというのはありますが)
Dockerコンテナを開発環境に組み込む3パターン
ではそんなDockerコンテナの開発環境への活用ですが、以下のように3パターンぐらい考えることができます。
パターン1: 依存しているものを1つでもDockerコンテナにしてみる
例えば開発にelasticsearchを使っていたら、開発機にインストールしているelasticsearchをDockerコンテナに置き換えてみましょう!
docker run -d -p 9200:9200 elasticsearch
といった感じで使っているポート番号と同じポートでバインドするようにして起動すれば今までと同様に開発をすることができます。
汚れてきて気に食わなくなったらコンテナを落としてみましょう。コマンド1つできれいさっぱり、メリットにあげた「揮発性」の素晴らしさを体感できることでしょう。
パターン2: docker-compose.ymlを書いてみる
パターン1では開発環境の一部に単体のDockerコンテナを使用しましたが、さらに踏み込んで docker-compose.yml を書いてみましょう。今までいちいちdockerコマンドを叩いていたのが、docker-compose up -d
で済みますし、何より開発環境に必要なコンテナ一式を一挙に管理できる のが便利です。
パターン3: 開発環境そのものをdockerコンテナ化する(devcontainer)
パターン2を使っていると自身がコードを書いている開発環境自体もDockerコンテナにしたくなるはずです!
開発環境自体をDockerコンテナ化することによるメリットとしては、例えばコンパイラやツール類のバージョンごとにコンテナイメージを作成することで、使用するコンテナイメージを切り替えるだけでパッとコーディングする環境自体を変えられることが挙げられます。
実際にPersoniumというOSSの開発ではJava1.8とJava17で同時に開発が走った時期があり、また、同時にelasticsearchの6系と7系の切り替えを検討するといったそれなりに環境に対してもインパクトのある改修を行ってきたんですが、それぞれDockerコンテナを用意して
みたいな4パターンを駆使して開発することがありました。これを手動でやっていたとすると、PATHだったりポートだったりもうゴチャゴチャになってしまい効率はすごく落ちていたと思います。
さらにdocker-compose.ymlにコーディングする環境自体を入れてしまうことによって得られるメリットとして、dockerネットワークが開発環境に活用できることがあります。いままで、開発環境がホスト側にある前提で構築してきた「パターン1」「パターン2」では、ホストへのポートのマッピングが必須でした。例えば、elasticsearchを3パターン用意するとしたら、9201, 9202, 9203といった感じで開発環境のパターンごとにポートを決め打ちで準備 する必要がありました。これってすごく面倒で、開発環境の設定も変えなきゃいけなくなります。
普段意識することは少ないんですが、composeを使用するとdocker-compose.ymlファイル(プロジェクト)ごとに dockerネットワーク というものが作成されています。dockerコンテナ間で通信をするときはコンテナごとに設定されたホスト名で通信できると思うんですが、これはdockerネットワーク単位で管理されます。つまり、A/docker-compose.yml
と B/docker-compose.yml
で別々のdockerネットワークが作成されていると、Aのコンテナ内で名前解決できるコンテナはAに閉じ、B内のコンテナに向くことはありません。
よって、必要な開発環境のバリエーションに合わせて「開発環境入りのdocker-compse.yml」を分けてしまえば、開発環境からは「同じcomposeファイルのxコンテナ9200番ポート」という設定のまま開発を行うことができ、また他の開発環境から独立して開発を進めていくことができるのです。
例: 実際の開発環境
と、ここまで書いて実践例もないのもアレかなと思い、拙作で恐縮ですが、自分がPersoniumというOSSの開発に使用しているdevcontainerについて紹介したいと思います。
devcontainerは、Visual Studio Codeでコンテナを使用した開発環境を構築できる拡張機能です。
Personiumの開発環境構成
Personiumではバックエンドに以下のようなものを使用しています。
また、バックグラウンドでタスク実行を行うエンジンと呼ばれるものがあります(Tomcat上で動かします)。
- personium-engine (タスク実行エンジン)
動かし方
それでは使い方を説明します。
まずは開発環境を配置したいディレクトリで以下のコマンドを実行して、必要なソースコードを揃えます。
git clone https://github.com/yoh1496/personium-core-dev.git git clone https://github.com/personium/personium-core.git git clone https://github.com/personium/personium-plugins.git git clone https://github.com/personium/personium-plugin-base.git git clone https://github.com/personium/personium-lib-es-adapter.git
実行することで、以下のようなディレクトリ構成になることを確認してください。
┗┳ /personium-core-dev ┣ /personium-core ┣ /personium-plugins ┣ /personium-plugin-base ┗ /personium-lib-es-adapter
(とりあえずpersonium-coreが依存するリポジトリを全部入れてますが、personium-core-devとpersonium-coreだけでも大丈夫です)
次に、クローンしたpersonium-core-dev フォルダをvscodeで開きます。開けたら、左下の緑色のボタンをクリックし、「Reopen in container」をクリックします。
すると、vscodeの画面が更新され、左下には「Dev Container」と表示されていると思います。これはpersonium-core-devフォルダ内の.devcontainer/devcontainer.json が読み込まれ、指定されたコンテナにアタッチされている状態を示します。
あとは煮るなり焼くなり開発することができるというわけです。
ちょっと解説
なぜこういう構成に至ったのかなどは過去記事を書いているのでそちらを参照してください。
当時に比べたらvscodeの.devcontainerは格段に取り回しやすく(以下の記事参照)なっており、ところどころ書いてあることが違ったりしますが根幹はあまり変わっておりません。
いつの間にかVSCode の Remote - Containersが超絶パワーアップしていた件【docker client 不要】 - いしぐめも
devcontainerのメリット: 開発環境構成のコード化
と、ここまでコンテナを開発環境に使用するメリットと実際に使用してみた例を紹介しましたが、もっとも重要なdevcontainerの利点の紹介が抜けていました。正直、ここまでの説明ではdocker-composeでコンテナあげて、アタッチしてVimで開発するのと何が違うの?という話にもなってしまうかなと思います。
コンテナを用いた開発環境を整える1つの方法として「devcontainer」を紹介しましたが、devcontainerの本当の利点は「VSCodeを使用した開発環境構成一式をコード化できる」 点にあると思っています。
今回devcontainerを使うことで、vscodeとdocker環境を整えることができさえすればボタン一つで開発環境構築を行えることを紹介しました。言ってみれば自動化なわけですが、vscodeとdockerが動く環境であればどこでも開発できるのです。
また、それだけでなく開発環境構築を自動的に行えるようにすることで、今までにあったような躓きやすいステップバイステップの開発環境構築ドキュメントとは別の手段を提供できるようになります。Personiumプロジェクトでも、開発環境の構築方法をドキュメントとして公開していますが、これを初学者に順に追ってもらうのはとても大変で、おそらくこの時点で挫折してしまう人も多いのではないかと思います。
その点、devcontainerを使用した開発環境構築では、ボタン一つで開発環境が整うのです。つまり、ちょっと興味を持っただけの1OSSを開発するためにJavaの開発環境構築やらなにやらといったノウハウを身につける必要がなくなります。プログラミングの最初のハードルは開発環境構築だと言われている(著者調べ)中で、ここが簡単になるのは非常に助かるんじゃないでしょうか。
GitHub Codespaces
は?何言ってんだvscode導入もdocker環境構築も難しいじゃねえか!!
というご意見もあるかなと思います。ごもっともです。
そもそも開発環境用のリソースを準備できないんだが??
ごもっとも(?)です。
そういうケースでも、devcontainerを使った開発環境構築をできるようにしておくことで使える、便利なサービスがあります。
GitHub Codespaces です。
このサービスはGitHub上で使用できるSaaSサービスで、リポジトリのソースコードから開発環境がVM上に構築され、ブラウザから操作できるというものです。
ついに「vscodeとdocker環境を揃える」という手間も必要なくなってしまいました。後はお金を払うだけ!!! (個人向けのベータ版は現在無料で使えるみたいです。)
こんな便利なサービスが使えてしまうなんて、我々は未来に来てしまったんだな~と感じずにはいられませんね。
そしてついについに、このCodespacesも全ユーザーに開放され、月に無料で60H使えるようになるらしいですね。素晴らしい!
終わりに
だらだらと書いていたらまとまりのつかない記事になってしまいましたが、Codespacesが無料開放というビッグニュースが出てしまったので、今まで下書き状態だったのを公開します。
devcontainer便利なので是非使ってみてください。他にもこんな便利な使い方あるよ~だとかこうするといいよ~といった内容があればコメントなどいただけると嬉しいです。Personium参加してみたいとかそういうのも大歓迎です。
ではでは、よいdevcontainerライフを
Windows 8.1 でも VSCode で Remote - Containers したい!
Visual Studio Code の Remote - Containersですが、Remote - SSH と組み合わせることで、リモートのDockerホストを用意すればクライアント側にdockerが入っていなくてもdevcontainerを使用することができるようになりました。
詳細は以下に記事にしたのでそちらを参照してください。
これってつまり、Windows 8(8.1)などのWSL2が導入できないようなOSでもバリバリdocker使って開発ができるというわけです。
スゴイ。
注意点
というわけで、Windows 8や8.1でやるにあたって注意点を書きます。
SSHにパスを通す
Windows 10 と Windows 8 の大きな違い。
それは sshコマンドが標準で入っていない 点です。今やSSHDサーバーもWindowsのオプション機能から追加できるようになっていますので、便利なものです。その点Windows 8は不便ですねえ。
というわけで、OpenSSHを適当なフォルダに展開してパスを通しましょう。
上記サイトではWindows10で手順が書かれていますが、Windows8でもだいたい同じです(環境変数のダイアログが不便なぐらい?)。
終わりに
画期的ですね(注意点がもう少しあるかと思ったけど1個で終わってしまった)。Windows 8でdockerを使おうとするとVirtualBoxにdockerマシン作ってそこに乗り込む形でやらざるを得ませんでしたが、これならVirtualBoxの導入なんて不要ですね。
VSCode と OpenSSH さえあればよかったんやー。
Java 8 から Java 17 への移行メモ
私が携わっているOSS「Personium」のJava 8からの移行に係るトラブルに対しての対処を書いていきます。Personiumでは Java 8 → Java 11 → Java 17 というステップで移行を進めていく方針になっていて、今回は「Java 11 → Java 17」を行った際のメモです。よって「Java 8 → Java 11」で取り切れなかった不具合を含んでいる可能性も多分にありますのでご了承ください。
目次
- 目次
- Unsupported class file major version 61
- java.lang.reflect.InaccessibleObjectException
- It is forbidden to use algorithm ... 問題
- Jerseyのバージョンが古すぎる
- 終わりに
Unsupported class file major version 61
こんなエラーが出まくりました。
Caused by: java.lang.IllegalArgumentException: Unsupported class file major version 61 at org.jacoco.agent.rt.internal_1f1cc91.asm.ClassReader.<init>(ClassReader.java:184) at org.jacoco.agent.rt.internal_1f1cc91.asm.ClassReader.<init>(ClassReader.java:166) at org.jacoco.agent.rt.internal_1f1cc91.asm.ClassReader.<init>(ClassReader.java:152) at org.jacoco.agent.rt.internal_1f1cc91.core.internal.instr.InstrSupport.classReaderFor(InstrSupport.java:247) at org.jacoco.agent.rt.internal_1f1cc91.core.instr.Instrumenter.instrument(Instrumenter.java:86) at org.jacoco.agent.rt.internal_1f1cc91.core.instr.Instrumenter.instrument(Instrumenter.java:118)
上記は jacoco のバージョンが低い時に出たエラーなんですが、surefireのバージョンが低いことでも同様のエラーが発生しました。
というわけで、こちらの問題に対しては pom.xml
に Java 17 対応の jacoco, surefire のバージョンを記載してあげることで解決しました。
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <version>2.22.2</version> </plugin>
<plugin> <groupId>org.jacoco</groupId> <artifactId>jacoco-maven-plugin</artifactId> <version>0.8.7</version> </plugin>
(バージョンは一例です。)
java.lang.reflect.InaccessibleObjectException
こういうやつです。
java.lang.reflect.InaccessibleObjectException: Unable to make field protected volatile java.io.InputStream java.io.FilterInputStream.in accessible: module java.base does not "opens java.io" to unnamed module @hogehoge
どうも調べるとJava 17ではより高位なAPIがあるので、今までのローレベルなAPIは叩くなよという例外だそうです。
とはいっても、テストではゴリゴリそのAPIを使ってしまっているみたいで仕方ないのでテスト時オプションで例外的に許可してあげます。以下のように surefire の実行時引数に --add-opens
で必要なパッケージを列挙します。
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <version>2.22.2</version> <configuration> <argLine> --add-opens=java.base/java.io=ALL-UNNAMED --add-opens=java.base/java.lang=ALL-UNNAMED </argLine> </configuration> </plugin>
文法的なものはドキュメント(Java Platform, Standard Edition Oracle JDK 9移行ガイド, リリース9)を参照していただくとして、例外の書式が以下だったとすると、
module <MODULE> does not "opens <PACKAGE>" to <TARGET_MODULE>
以下のように書けば素直に消えてくれるはずです。(TARGET_MODULEがunnamed moduleだった場合)
--add-opens=<MODULE>/<PACKAGE>=ALL-UNNAMED
ただこの、java.util
java.util.regex
java.util.zip
など複数パッケージに対して行う必要がある場合は以下のように愚直に書いていくしか無さそうでした(やり方あったら教えてください)。
--add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.base/java.util.regex=ALL-UNNAMED --add-opens=java.base/java.util.zip=ALL-UNNAMED
コレ、1件解決したら次の1件、といった感じで芋づる式に出てくるので根気よく対処しましょう。ゆくゆくは根本対処を何かしたいところ・・・
It is forbidden to use algorithm ... 問題
SHA-1使ってたコードが軒並み動かなくなった問題です。
It is forbidden to use algorithm http://www.w3.org/2000/09/xmldsig#rsa-sha1 when secure validation is enabled
こちらは別記事にしたのでそちらをどうぞ。
Jerseyのバージョンが古すぎる
今まで Jersey 1.10とかいう化石のようなものを使っていたので、以下のようなエラーが出てしまっていました。
java.lang.IllegalArgumentException at jersey.repackaged.org.objectweb.asm.ClassReader.<init>(ClassReader.java:170) at jersey.repackaged.org.objectweb.asm.ClassReader.<init>(ClassReader.java:153) at jersey.repackaged.org.objectweb.asm.ClassReader.<init>(ClassReader.java:424)
というわけで一気に2系の2.36まであげました。
1から2への移行ということで、Jersey Test Frameworkの仕様も変わっていましたが、Grizzly + Servlet を使ったテストを実装するにあたって以下のサイトがとても参考になりました。
終わりに
以上が、PersoniumをJDK17に移行させるにあたって詰まった主な内容でした。どなたかの参考になれば幸いです。
JDKアップデートしたらsecureValidationでテストがコケたので暫定対処をしました
JDK17へ移行しようとテストコードを流していたらRSA-SHA1を使用している部分で以下のエラー。
It is forbidden to use algorithm http://www.w3.org/2000/09/xmldsig#rsa-sha1 when secure validation is enabled
どうもsecureValidationが有効な状態だと、特定のアルゴリズムの仕様が許可されないセキュリティエンハンスのせいらしい。
以下は、Sean Mullan氏の記事。
--
本来であれば、正しいアルゴリズムを採用するようにコード側を変更すべきですが、いかんせん影響範囲が大きい部分だったりするといったんはこのアルゴリズムを許可してテストを通るようにして、アルゴリズム変更はまた別で行うべきかなと考え、暫定で許可することとしました。
以下は許可の方法です。
secureValidationで禁止するアルゴリズムを変更する
securyValidationの際に禁止されるアルゴリズムは、java.security
に列挙されています。
Eclipse-Terumin JDK17
のコンテナイメージでは、以下の場所にありました。
/opt/java/openjdk/conf/security/java.security
これを覗くと確かに jdk.xml.dsig.secureValidationPolicy
というポリシーがあり、それを見るとSHA-1とかもあります。
直接編集してしまってもいいんですが、さすがにシステム全体のポリシーを変えてしまうのもアレなので、テストを流したり実行したりする場合にのみこの変更を適用するようにします。
まずは適当な場所に java.security
ファイルを作成します。(今回、自分の場合はプロジェクト直下においてしまいました)
そして、テスト実行する際に以下のオプションを指定するようにします。VSCodeでテストする際のvmArgsに書いたり、pom.xmlのsurefireのargsに書いたりしてみました。
-Djava.security.properties=./java.security
java.securityを作成した位置に応じて適宜読み替えてください。そして、java.source
の内容をもともとのjava.sourceからコピペし、必要なアルゴリズムをリストから削除して以下のようにしました。
jdk.xml.dsig.secureValidationPolicy=\ disallowAlg http://www.w3.org/TR/1999/REC-xslt-19991116,\ disallowAlg http://www.w3.org/2001/04/xmldsig-more#rsa-md5,\ disallowAlg http://www.w3.org/2001/04/xmldsig-more#hmac-md5,\ disallowAlg http://www.w3.org/2001/04/xmldsig-more#md5,\ disallowAlg http://www.w3.org/2007/05/xmldsig-more#sha1-rsa-MGF1,\ disallowAlg http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha1,\ maxTransforms 5,\ maxReferences 30,\ disallowReferenceUriSchemes file http https,\ minKeySize RSA 1024,\ minKeySize DSA 1024,\ minKeySize EC 224,\ noDuplicateIds,\ noRetrievalMethodLoops
削除したのは以下の3行です。
disallowAlg http://www.w3.org/2000/09/xmldsig#sha1,\ disallowAlg http://www.w3.org/2000/09/xmldsig#dsa-sha1,\ disallowAlg http://www.w3.org/2000/09/xmldsig#rsa-sha1,\
(ここは必要なアルゴリズムに応じて読み換えてください。)
動作確認
ここまでできたら、テストを流してみます。流れればOK。
おわりに
unmarshalXMLSignature
実行する系テストの以上系でエラーが出て、JDKのバージョンアップで javax.xml.crypto.dsig.XMLSignatureException
の内容が変わってしまったのか~~~?!なんて思ってたらもっと根深いところに原因があったみたいです。
どなたかのご参考になれば幸いです。
Azure Functions の開発にはdevcontainerを使うと便利だった
Pythonを使用したAzure Functionsの開発に Visual Studio Codeのdevcontainerを使用しようというお話です。
はじめに
どうも、devcontainer大好きdevcontainerおじさんです。
最近Azure Functionsで普段は使用しないpythonを使って開発してみようと思い触ってみたんですが、いきなり npmでazure functions core toolsをインストール とか言われて、オイオイpython開発なのにnodeが要るなんて聞いてないぜとなったので、結構Azure Functions (Python)の開発環境整えるのって面倒くさそうだなと感じました。
そこでdevcontainerを使って開発環境を整えるとすごく便利ということがわかった(というかMicrosoftが便利なコンテナイメージを提供してくれている)ので、共有です。
Microsoftが提供してくれているAzure Functions用のコンテナ
提供されているコンテナイメージの一覧はAzure公式リポジトリから参照することができます。様々な種類があるうえに、Dockerfileも公開されているので参考にされてみてはいかがでしょうか。
今回は、core toolsが同梱されている 4-python3.9-core-tools
を使用してみました。
docker run --rm -it mcr.microsoft.com/azure-functions/python:4-python3.9-core-tools bash
上記コマンドで bash で試しに入ってみましたが、もちろんPythonは使用できますし、これからデプロイしたりするのに使用する Azure functions core tools も導入されていることがわかります。
始め方
ではこのコンテナイメージを使って開発する流れをご紹介します。始め方はとても簡単で、vscodeのメニューをポチポチいじるだけでdevcontainerを使った開発を始めることができます。
以下の手順はRemote - SSHで接続したリモート開発環境でもOKです。詳しくは↓
開発用フォルダをオープン
まずはFunctionsのソースコードが入るリポジトリ(フォルダ)を開きます。Functionsを作るのはこれから、という人は空のフォルダで大丈夫です。(例では適当にfuncapp001というフォルダを掘ってみました。)
devcontainerを準備する
というわけで次にdevcontainerを導入するんですが、ここではvscodeのウィザードを使用していきます。
左下の緑色のボタンをクリックし、開いたリモート関連のメニューから「Add Development Container Configuration Files...」をクリックします。
すると追加するdevcontainerの種類を選ぶメニューが出るので、「Show All Definitions...」をクリック。
新たに開いた画面でコンテナイメージ一覧がずらっと出るので、「Azure Python 3」などと入力します。
表示された「Azure Functions & Python 3」を選択します。
すると、.devcontainerフォルダが作成され、その中に devcontainer.json
と Dockerfile
が格納されていることが確認できると思います。 Dockerfile
に FROM mcr.microsoft.com/azure-functions/python:4-python3.9-core-tools
など、意図したコンテナイメージを使用する文言が含まれていることを確認します。
その状態で、左下の緑色のボタンをクリックし、「Reopen in Container」をクリックすると、立ち上がったdevcontainerに今のフォルダが開かれた状態でvscodeが再起動します。
あとはこれを使って通常通り開発していく、といった感じです。
終わりに
やはりdevcontainerは開発環境を整えるのにとても便利なツールですね。特に普段使用しないプログラミング言語を選択した場合など戸惑うことが多く、どうしても作っては壊しを繰り返すことになると思うので、コンテナでひとまとまりにして何度でもやり直せるのはとてもグッドだなと思いました。
devcontainer × docker-compose で共有サーバーで複数人開発を行う
Visual Studio Code の devcontainer (Remote Containers拡張機能)を使用した共有サーバーでの開発について書きます。
課題感
プロジェクトで共通のサーバーを用意してみんなで使いまわそう!みたいになった時、ミドルやDBの管理はどのようにしていますか?
開発の中では以下のような要望がでてくると思います。
- ミドルを複数バージョンで試したい
- 開発の途中でDBをいったんリセットしたい
- 同じ構成でも複数ブランチのソースコードを試したい
複数人で共通サーバーを使ってこれをやろうとした場合、ポートの割り当てを1つ1つ分けて設定するなど七面倒くさい管理が発生してしまいます。
- 開発に必要なサーバー一式を管理できるようにしたい
- 複数ユーザーでそれを使えるとなおよい
という課題感がありました。
課題解決への筋道
docker-compose で開発環境に必要なサーバー一式をそろえる
「課題感」にも書きましたが、複数構成のミドルウェアを1つのホストで動作させるのは骨が折れます。1つのミドルに対して複数のコンポーネントが依存関係にあったりすると地獄ですね。
それを解決するのがDockerに代表されるコンテナ技術であり、それらのコンテナをまとめて管理できるのが docker-compose
です。「docker-compose.prod.yml」と「docker-compose.dev.yml」といった感じで複数 docker-compose を用意して、プロダクション環境に合わせた開発環境、新しいバージョンを取り入れたリリース前の開発環境みたいな感じで切り替えるのはよくあるケースだと思います。
docker-composeのプロジェクトファイルに記述されたサービスのポートをlocalhostにバインドして、localhost:9200に対してリクエストすることでelasticsearchコンテナのAPIを叩く、みたいなやつですね。
docker-compose のネットワーク構成を使用しよう
上記のやり方ではlocalhostのポートをバインドしてしまっているので、結局複数パターンの開発環境を立ち上げるためにはポート管理を手動で行う必要がでてきてしまいます。これではdocker-composeを使って環境を作ったり壊したりするのは楽になったものの、複数環境の比較が楽になったとは言えません。
そこで活用したいのが、dockerのネットワーク機能です。dockerのネットワークはコンテナ間の通信を分離する仮想的なネットワークで、同一ネットワークに属するコンテナ同士はそのコンテナのポートで通信をすることができます。つまり、開発者が操作する開発環境をdockerのネットワークにつなげられるようにしてしまえば、localhostのポートにバインドする必要なくコンテナと通信することができます。(実際にはランダムなポートがバインドされます)
docker-compose は、プロジェクトごとにコンテナ用のネットワーク(default)を構成し、各コンテナは自動的にそのネットワークに接続されます。つまり、docker-compose内に開発用のコンテナをサービスとして定義し、そこに乗り込んで開発することができれば、プロジェクトごとに一意のホスト(コンテナ)名・ポートで通信することができるのです。つまり、プロジェクトAとBで「elasticsearchホストの9200ポート」という見かけ上同じでありつつ別のコンテナと通信する、ということができます。
devcontainerを使用することで、docker-composeを使って構成されたサービスにアタッチして開発することができます。
共有サーバーでも使用できます
さらにうれしいのが、最近(2021年11月)、リモートのdockerホストに対してのdevcontainer使用が Remote SSH 拡張機能との組み合わせで行えるようになったことです。それについては以下に記事を書きました。
これができるようになったことで、共有サーバーに対して複数ユーザーでログイン、各々のユーザーがポートの管理無しに開発を行うみたいなことができるようになったのです。
基本的な設定方法
使い方については、vscodeのドキュメントが充実しているので、こちらを参照してください。
Tips&トラブルシューティング
EACCES: permission denied, mkdir '/tmp/vsch…
どうも複数ユーザーでの使用は想定されていないらしく(?)、各ユーザーの VSCode devcontainer の一時ファイルが /tmp 直下に作られてしまいます。それぞれオーナーは作成ユーザーとなるため、Aさんが試した後にBさんが使おうとしたりするとパーミッションの関係でエラーが出ます。
GitHub上でもエラーは報告されており、2022年6月現在解決されていませんが、回避策があります。
EACCES: permission denied, mkdir '/tmp/vsch · Issue #2347 · microsoft/vscode-remote-release · GitHub
mkdir -p /tmp/$USER export TMPDIR=/tmp/$USER
上記コマンドを ~/.bashrc
に書くことで、TMPDIRが /usr/$USER になるため、devcontainerの一時ファイルがユーザーごとに異なるフォルダに作成され、前に述べたような競合が起こらなくなります。
とりあえずは上記対応で問題は発生しなくなりますが、早いこと公式に対応してもらえると嬉しいなと思います。
他のコンテナを触りたい
基本的にdevcontainerで触れるのはアタッチされているコンテナのみです。つまり他のミドルやDBに対して何かしらAPIを叩きたいときはdevcontainerでいったん開発用コンテナに入って踏み台的にAPIを実行する必要があります。
ただ、それだとちょっと使いづらいので、開発用コンテナのlocalhostへの通信を特定のコンテナにリレーします。
通常のdevcontainerのセットアップでは、コンテナのcommandには /bin/sh -c "while sleep 1000; do :; done"
として無限に待機するように設定しますが、そこを socat
を実行します。ここでは私が開発に使ってるdevcontainerの設定を示します。
personium-core-dev/docker-compose.local.yml at main · yoh1496/personium-core-dev · GitHub
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:61616に対してはactivemqコンテナの61616に転送したり、9200ポートをelasticsearchの9200ポートにリレーするといった設定を同時に行うことができます。
socat
はコンテナを落とさない限り実行されますので前の設定のような無限ループは必要ありません。
終わりに
devcontainer × docker-compose で快適開発ライフを!
いつの間にかVSCode の Remote - Containersが超絶パワーアップしていた件【docker client 不要】
以前、「Docker Desktop for Windows」無しで Visual Studio Codeで「Remote - Containers」を使用するために Docker client を手動ダウンロードするお話を書きましたが、それがもはや不要だそうです。
はじめに
なんとなく Remote - Containers のドキュメントを読んでいたら以下のような記述を発見しました。
If you are using a Linux or macOS SSH host, you can use the Remote - SSH and Remote - Containers extensions together. You do not even need to have a Docker client installed locally.
どういうことか
超絶ハイパワーなサーバー上でコンテナを立ち上げて開発したいと思ったこと、あると思うんですが、以前までの拡張機能では単体でそういったことが行えず、ローカルに導入したDockerクライアントを使ってリモートのDockerホストを使用するという方法を取る必要がありました。
それが今回(?)、アップデートによって Remote - SSH と組み合わせて使うと、リモートで「Reopen in Container」するとリモートでコンテナが立ち上がり開発できるようになったみたいです!すごい!!!
ちなみに、これもしかして実は昔からできて、自分の勘違いだったんじゃ???って思いましたが、Webアーカイブから見るに、以前のドキュメントにはこのような記載はなく、新規に追加された機能っぽい です。
と、テンション上がっていたんですが、この機能 2021年11月に追加されたみたいですね・・・
もっと話題になればいいのに!!!というか自分のアンテナが低すぎか!!!!
メリット
ローカルマシンにdockerクライアント不要
これはうれしいですね。VSCode入れられればどこでもコンテナ使った開発ができると。
「devcontainer使うためにdocker導入してくださいね~~~~」っていうのが必要なくなります。
docker-compose.yml 不在問題がなくなる
ローカルマシン上のdocker-compose.ymlを使用してリモートのDockerホストにコンテナを立てるとどうなるか。
リモートからは見えない位置にdocker-compose.ymlがあるので、リモートから docker-compose down
できなくなります。
それが今回、リモートホスト上のdocker-compose.ymlを使用してそのマシン上のDockerホストにコンテナを立てることになるので、そういった問題が発生しなくなります。
手元のファイルシステムをコンテナのボリュームにマウントできる
今までの方法ではdevcontainerがあるフォルダはローカルマシンで、実行環境はリモートという構成になっていたためファイルシステムをマウントすることができず、代わりにvolumeを作成してマウントし、その中にgit cloneする必要がありました。
やってみればそれの何が不便というわけでもないんですが、devcontainerとソースコードをひとまとめに管理できるというのが良いかなと思います。
終わりに
あんまりDockerクライアントのみをローカルマシンにインストールしてリモートのDockerホストを使用するという方法を取ってる人はいないのかもしれませんが、個人的にこれがビッグニュースだったので書いてみました。