カテゴリー
Dockerコンテナ内で独自にDevContainersっぽく使えるAngularプロジェクト環境を構築してみる
※ 当ページには【広告/PR】を含む場合があります。
2023/09/25

Dockerコンテナを開発環境として使っているエンジニアの方にとっては、VSCodeの「Dev Containers」エクステンションをバリバリ使っている方も多いでしょう。
例えばAngularアプリで複数の類似プロジェクトを開発が頻発する場合、共通のnpmパッケージを一つにまとめ上げた上で、プロジェクトのビルド効率性を底上げして、リソースだけを一元管理したい時があります。
特にAngularでのSPAプロジェクトの開発では、npmパッケージのインストール後はどうしても
node_modules
1つ2つ程度なら、まだ個別のプロジェクトとして管理・開発しても良いですが、ブロジェクトの数が乱発してくると、ほぼ同じライブラリー群を持った
node_modules
同じnpmパッケージを使う複数のプロジェクトを一つに束ねて整理できるようになったら、デスクサイズのサイズの大幅な圧縮が可能になります。
そこで、今回はDockerコンテナ起動時にマウントするボリュームを設定して、「Dev Containers」のコンセプトに近いような複数のプロジェクトを横断的に管理・開発できるnodejsプロジェクトの構築方法を検討していきます。
はじめに
「Dev Containers」を使ったことの無い人のために、すこしおさらいしておきましょう
Dev Containersは何がいいのか?
VSCoed公式からのDev Containersの仕組みを示した模式図を見ると、おおよその概要が掴みやすいかもしれません。
947x384

+ VSCodeにエクステンションと「devcontainer.json」と呼ばれる設定ファイルを入れるだけで、
あとは自動でDockerコンテナの開発環境がバックグラウンドで準備される
+ 開発コンテナには必要なライブラリやランタイムをDockerのVolume内で隔離・管理できる
+ プロジェクトのリソースファイルと開発環境を分離できるので、
類似のプロジェクトでは開発コンテナの統一・再利用が可能となる
どうでしょう。
一見、割といい事づくしな素晴らしい開発環境が簡単に構築できそうな話に聞こえてきます。
ですが、「Dev Containers」を使う際には少しだけ気をつけるべき落とし穴があります。
DevContainersを使う注意点
公式の
このバインドマウントを使うということが、場合によっては厄介に感じるかもしれません。
以下の技術記事で実際の「バインドマウント」と「ネームドボリュームマウント」でのアクセス速度の比較をされていますので、興味があれば覗いてみてください。
書き込み・読み込み速度の詳しい比較が避けますが、スペックの低いパソコンでDevContainersを使ってみると、なにかにつけて
個人的な経験で言えば、低スペックのPC上でDevContainersを使ったときに、Nodejsアプリなら
yarn install
cargo build
もちろん対処法に書かれているように、
Named Volume
Dockerで単一のNodejs開発環境で複数のAngularプロジェクトを束ねる
今回説明するテクニックを使うことで、Angularに限らずNodejsベースのフレームワーク等で
node_modules
このブログでは、とりあえず「Angular」を題材に話を進めていきます。
angular.jsonかDockerのvolumeのどちらを使った方が良い?
そもそも、
angular.json
これは複数のAngularリソースを共存させるプロジェクトはCLIコマンドと
angular.json
例えば、
$ ng new my-workspace --no-create-application
$ cd my-workspace
$ ng generate application app-a
$ ng generate application app-b
$ ng generate application app-c
#...
としてこのように設定した時、
angular.json
projects
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": {
"app-a": {
...
},
"app-b": {
...
},
"app-c": {
...
},
//...中略
},
//...中略
}
一見これだとすべてのサブプロジェクト(ここでは
app-a
app-b
app-c
node_modules
ただ実際には、下の模式図のようなプロジェクト構造になっています。
750x783

結局はサブプロジェクト毎に格アプリのビルドに最適化・専有化した
package.json
node_modules(npmパッケージ)
サブプロジェクトを一つの飲食店舗として捉えると、まさにプロジェクト全体は「フードコートのような食の複合施設」になってしまいます。
つまり入店する店舗が多ければそれだけ大規模なリソースが必要になる、ということです。
これは今回目指しているような
そこで登場するのが「DevContainer」的考え方の延長で、「Dockerの定義済みボリューム」を上手く使うやり方になります。
DockerボリュームでNodejsアプリ用のワークスペースを整える
Dockerでのホスト側ファイルシステムのマウントについては先行して以下の記事で説明していました。
すこし複雑に感じられる場合には、ここからはDockerでの「ボリューム」を理解してからお読みください。
Dockerボリュームを上手く使って、一つのワークスペースに
node_modules
さきほどのAngular独自のマルチブロジェクトと比較したイメージ化してみると以下のように考えられます。
750x424

先述したプロジェクト構造とは異なり、各プロジェクトはリソースのみを保持し、対してDockerコンテナ側からアクセスできるボリューム上に
package.json
node_modules
そしてDockerコンテナ(開発環境)側は、ワークスペースにマウントされたリソースが"どれか"、に応じてアプリをビルドします。
その意味では、持ち込んだ食材(リソース)によって、自在に商品を変えて対応する「移動販売のような変幻自在な屋台」のようなものです。
この場合、
node_modules
他方、デメリットにあたるかは分かりませんが、Dockerの仕組みを良く理解しておく必要があります。
コンテナ技術の知識はあらかじめ理解した上でないと、Dockerのバインドマウントやボリュームマウントの使い分けに、戸惑ってしまうかもしれません。
実践〜Dockerボリュームでワークスペース構築
大体のコンセプトを把握していただいたところで、docker-composeによるワークスペースの実装を見ていきます。
まずは必要最低限の
docker-compose.yml
version: '3.9'
services:
app:
#...他のタグは省略
volumes:
#👇リソースはprojectフォルダにバインドマウント
- ./:/app/project
#👇念の為node_modulesをバインドマウントから除外
- /app/project/node_modules/
#👇cacheボリュームをworkspaceフォルダにマウント
- cache:/app/workspace
#👇作業ディレクトリをワークスペースのフォルダに指定
working_dir: "/app/workspace"
volumes:
#👇ワークスペースとなるボリューム名(名前は任意)
cache:
さて、今回のお話の中で関係がある設定は、
services
volumes
working_dir
volumes
Dockerコンテナ側のファイル構造ですが、今回は説明の便宜上Dockerコンテナ内の
/app
ここでは
/app
workspace
project
/app
├── workspace (Volumeマウントかつ作業フォルダ)
└── project (Bindマウント)
他方で、実際にはもう少しファイル数も多いと思いますが、説明のためにシンプル化した以下のようなプロジェクトがあったとします。
.
├── app-a
│ ├── src (リソースフォルダ)
│ ├── ...
│ ├── package.json
│ └── docker-compose.yml (上で説明した内容を適用)
├── app-b
│ ├── src (リソースフォルダ)
│ ├── ...
│ ├── package.json
│ └── docker-compose.yml (上で説明した内容を適用)
└── app-c
├── src (リソースフォルダ)
├── ...
├── package.json
└── docker-compose.yml (上で説明した内容を適用)
ブロジェクト側のほうはリソースの準備だけでOKで、個別に
yarn install
なお、各プロジェクトの
package.json
dependencies
このプロジェクトを利用する初回は、ブロジェクトのどれかの
package.json
$ cd app-a
#👇初回はコンテナにアタッチモードで中に入って作業
$ docker-compose up -d && docker-compose exec app bash
###...アタッチモードでワークスペースに入る
#👇プロジェクトのpackage.jsonをワークスペースへ持ってくる
$ cp ../project/package.json ./package.json
#👇package.jsonからnpmパッケージをインストール
$ yarn install
これで、cacheと言うボリューム内に
node_modules
あとはコンテナで何を実行するかによりますが、
app-b
$ cd app-b
$ docker-compose up -d && docker-compose exec app bash
###...アタッチモード
#👇プロジェクトのリソースをワークスペースへコピー
$ cp -r ../project/ ./
#👇アプリをビルド・デバックサーバーを起動
$ yarn start
同様に
app-c
$ cd app-c
$ docker-compose up -d && docker-compose exec app bash
###...アタッチモードでワークスペースに入る
#👇プロジェクトのリソースをワークスペースへコピー
$ cp -r ../project/ ./
#👇アプリをビルド・デバックサーバーを起動
$ yarn start
でワークスペースにおいて共通化した
node_modules
もし、アタッチモードに入らずに
docker-compose up
docker-compose.yml
command
cp -r ../project/ ./
この辺は開発者側のひと工夫が要求されます。
まとめ
最後に首題にて「Dev Containersっぽく使える」と表現したものの、実際の本家・
Dev Containers
基本的には、「Dev Containers」を使ったほうが何も考えなくて良いし開発も捗る、というのならそのまま使い続けるほうが良いでしょう。
あくまでも「Dev Containers」の挙動が極端に重かったり・機能が気に入らなかったりする場合には、今回紹介したDockerのマウントタイプを切り替えるテクニックを使って、複数のプロジェクトフォルダの構成を手動管理してみてください。
記事を書いた人
ナンデモ系エンジニア
主にAngularでフロントエンド開発することが多いです。 開発環境はLinuxメインで進めているので、シェルコマンドも多用しております。 コツコツとプログラミングするのが好きな人間です。
カテゴリー