この記事は WESEEK Advent Calendar 2020 13日目の記事です。
はじめに
みなさん docker は普段使っていますか?
docker いいですよね。Dockerfile や docker-compose.yml などのコードさえ用意して docker の実行環境さえ用意しておけば誰も簡単に同じ環境の構築ができます。
そんな docker の特性を活かし、めんどくさい開発環境の構築を一発で完了させ、しかも誰もが同じ環境で開発できる方法を紹介したいと思います。
VSCode の devcontainer とは?
VSCode の extension の Remote - Containers を使って立ちあげる開発環境のことを指しています。
公式ドキュメントは こちら
この仕組みを利用することで VSCode と docker の動く環境さえあればだれでもボタン数発で開発環境を構築することができ、すぐにコードを書き始めることができます。
利用方法
docker/docker-compose インストール
本題ではないので 公式 を見て各々の環境にインストールしましょう。
Windows の場合は WSL2 と Docker Desktop for Windows の組み合わせ、
Mac の場合は Docker Desktop for Mac を使うのが一般的かと思います。
Docker さえ動くようにしてしまえば OS の差異は Docker が吸収してくれるので同じ環境が構築できると言えるわけです。
VSCode 及び extension インストール
VSCode は 公式ページ よりダウンロードしてインストール。
必要な VSCode の extension は VSCode 左上の ブロックみたいなマークをクリックし、出てきた検索窓に remote
と打ち込むと画像のような extension 達が出てくると思います。
自分の VSCode にはすでにインストール済みなので左上に星マークがついていますが未インストールの場合は付いてないかと思います。
Remote - Containers
と Windows で WSL を使う場合は Remote - WSL
の両方をインストールしましょう。
起動方法
※上記必要なプロダクトのインストールは終わっている前提です。
前に自分が試しに触ってみた Blitz.js のプロジェクトの立ち上げを例にしてみたいと思います。
devcontainer 利用の確認
リポジトリの中身を見て行くと https://github.com/haruhikonyan/blitz-first/tree/master/.devcontainer こんなフォルダがあります。
リポジトリ内に .devcontainer フォルダとその中に devcontainer.json が入っていればそのプロジェクトでは VSCode と devcontainer を使って統一された開発環境を苦無く立ち上げて開発しているんだなと思ってよいと思います。
devcontainer 起動
Windows では WSL の中、Mac では適当なディレクトにリポジトリを clone します。clone したら VSCode を起動し、画面一番左下の ><
っぽいところをクリックします。するとスクショのようなメニューが出てくるかと思います。
すでに VScode でリポジトリを開いている場合は Remote-Containers: Reopen in Container
を選択します。
リポジトリを開いてない場合は Remote-Containers: Open Folder in Container
を選択し、リポジトリ root を選択しましょう。
すると VSCode が devcontainer.json の設定を読み込み、必要なコンテナの立ち上げなどが行われます。
実際にどんな処理が行われているかどうか気になる人は show log
をクリックするとコンテナの立ち上がる様子が確認できます。
しばらく待ち、必要なコンテナがすべて立ち上がるとコンソールが立ち上がります。(show log
を見た方は VSCode 下部の TARMINAL タブ右側 +
ボタンをクリックして新しくコンソールを開きましょう。)
あとはもう普段と同じように node であれば yarn
や npm
などを利用して開発環境を立ち上げるコマンドを実行すれば開発環境が起動します。
例の blitz.js では blitz start
を実行すると開発用のサーバが立ち上がり、localhost:3080
にアクセスすると画面が表示されると思います。
開発環境構築方法
※ あくまで自分が新しく開発環境を構築するために取る手法であって必ずこの手順を踏まなければいけないというわけではありません。
利用方法で例として出した blitz.js の開発環境構築をする方法を紹介します。
1. 立ち上げるコンテナを何にするかを決める
今回はフロントエンドからバックエンド、そして今話題の ORM である prisma を搭載したフルスタックフレームワークである blitz.js を動かすので node だけではなくデータベースも必要となります。
なので blitz 本体を動かすための node コンテナと、データベースを動かす DB コンテナの二つを用意することにします。
2. devcontainer.json と docker-compose.yml を作成して起動する
{
"name": "blitz",
"dockerComposeFile": ["./docker-compose.yml"],
"service": "app",
"workspaceFolder": "/blitz",
}
とりあえずひな形として devcontainer.json
作成
FROM mcr.microsoft.com/vscode/devcontainers/javascript-node:0-14
あとから拡張しやすいようにメインとなる node コンテナは Dockerfile にするとことにします。
ベースイメージは VSCode を作ってる microsoft が出してる devcontainer 用の node14 イメージを使うことにします。
version: "3"
services:
node:
build:
context: .
dockerfile: Dockerfile
working_dir: /blitz
volumes:
- ../:/blitz:delegated
- node_modules:/blitz/node_modules
tty: true
db:
image: mysql:8.0.20
volumes:
- mysql-data:/var/lib/mysql
environment:
MYSQL_USER: root
MYSQL_ROOT_PASSWORD: password
volumes:
node_modules:
mysql-data:
node は作った Dockerfile をビルドし、リポジトリを working_dir である /blitz へマウント。
node なので node_modules は必ずできることがわかっているので named volume にしておきます。
volume についての説明は後述します。
そして DB は mysql の現在の最新を採用。
DB のデータは named volume に設定。enviroment で適当にユーザ名、パスワードを設定しておきます。
開発環境なのでべた書きでもいいかなと思っている派です。
まずこちらで起動してみましょう。
ビルドが完了すると、
root ➜ /blitz $ node -v
v14.15.0
root ➜ /blitz $ npm -v
6.14.8
root ➜ /blitz $ yarn -v
1.22.5
という結果が取得できるかと思います。
3. コンテナ内で開発環境を構築する
新規作成なので blitz の公式 どおりにインストールしていきます。
$ npm install -g blitz
$ blitz new .
$ blitz start
~~
ready - started server on http://localhost:3000
blitz は node さえあれば立ち上がるようですね。
ここでほかのライブラリが必要であれば apt なりを利用して必要なライブラリをインストールしていき、目当ての開発環境が立ち上がる環境を構築していくことになります。docker とはいえ今回のベースイメージは元をたどれば ubuntu なのでローカルで構築するときとさほど変わりは無いのかなと思います。
しかしこのままの blitz の初期設定では prisma で使う DB が SQLite なのでせっかく用意した mysql は利用されていません。そのための設定変更をしていきます。
datasource db {
provider = "mysql"
url = "mysql://root:password@db:3306/blitz"
}
ここでのポイントはホストが db
となっていることです。devcontainer を立ち上げている docker-compose.yml の mysql の service 名は db
と名付けているので、同じ docker-compose で立ち上げたコンテナ間はデフォルトでサービス名をホスト名にして通信が可能です。
DB の設定変更した後忘れずに blitz db migrate
を実行しておきましょう
4. 各種ファイルを編集する
blitz の開発環境が立ち上がりましたがまだホストからのアクセスができません。docker-compose でポートフォワードの設定をしてあげる必要があります。
services:
app:
build:
context: .
dockerfile: Dockerfile
volumes:
- ../:/blitz:delegated
- node_modules:/blitz/node_modules
working_dir: /blitz
tty: true
ports: # これら追加
- 3000:3000
ports を設定して左下の ><
みたいなマークから Remote-Containers: Rebuild Container
を実行すると devcontainer の再作成が行われ、新しい設定が反映されます。
再起動が完了したらホスト側のブラウザから localhost:3000 へアクセスしてみましょう。
すると blitz の初期画面が出てくるはずです。もちろん DB との疎通が取れていればユーザ作成もできるはずです。
今回の場合は node さえあれば blitz は動いたので必須なのはポートの設定くらいでしたが、container に apt などを使って何かをインストールしていたらそれを Dockerfile に記述したりする必要があります。
そして後述する よく使う関連機能 tips
で紹介する devcontainer や docker-compose の便利な機能を追加していきましょう。
よく使う関連機能 tips
自分が多用したり工夫して使ってる(つもり)のオプションや設定を紹介します。
devcontainer.json
公式リファレンスは こちら
- extensions
devcontainer 初回起動時に指定した extension のインストールを行ってくれます。
あたりまえですが、たくさん指定すると初回起動時にすべてのインストールが走るため、起動は遅くなります。初回だけなのでそんなに気にする必要は無いと思います。
GROWI では こんな感じ に lint ツールや debug ツールなどの extension をデフォルトでインストールさせることで開発環境の統一をより強固な形にしています。 - postCreateCommand
devcontainer 起動時にコンテナを新規作成した後にコンテナ内で実行させるコマンドを指定することができます。
コンテナ作成時なので毎回起動時に実行されるわけではないので注意です。
このように 指定をすれば、初回起動時にyarn install
が実行され、node の package が自動的にインストールされます。
しかし自分のあずかり知らないところで大量のファイルをダウンロードすることになったりするので、好みに合わせて設定しましょう。 - initializeCommand
devcontainer 起動時にコンテナを新規作成する前に ホスト側 で実行されるコマンドを設定することができます。
自分の 試しに作った Blitz.js では サンプルの env ファイルを用意しといてそれをコピーするということを行っています。コピー先は gitignore しているので各々の設定を追加してもリポジトリにコミットされることはありません。
docker-compose.yml
公式リファレンスは こちら
-
env_file
ファイル名を指定するとそのファイルに記載された環境変数がコンテナにセットされる。 サンプルの env ファイルをを用意しておいてアクセス token などを開発者に書いてもらったり、前述の initializeCommand と併用してデフォルトの環境変数ファイルを初回にコピーさせたりして使っています。 -
volumes
ものすごく奥が深い設定項目。- リポジトリ自体のマウント
これをしないと devcontainer 内でファイルの編集ができないのでおそらく必須です。
試しに作ったBlitz.js の devcontainer 用の docker-compose.yml は .devcontainer に入っているので相対パスとして .. と指定するのがポイントです。
また、devcontainer での開発であればファイルの編集はコンテナ内で行うため、ホスト側との整合性を基本的に気にする必要が無いので一番高速である:delegated
を指定すると良いと思います。 - 外部ライブラリファイル群を named volume としてマウント
node であればnode_modules
、ruby であればbundle
といった外部ライブラリの保存場所を volume として指定してホストとの同期を断ち切ります。基本的にリポジトリには共有をせず直接いじることのいかつ、大量にある外部ライブラリのファイルはリポジトリのデータと一緒にホストと同期してしまうと、ファイルアクセスの度ににホストまで見に行かなくてはならず、スループットが著しく落ちる原因となります。docker for mac の場合はこれが顕著でよく mac の docker は遅いと言われる要因の一つだったりします。 また、named volume として設定しておくと、コンテナが削除されてもデータは保持し続けられるので、もし開発環境の設定が変わってコンテナを rebuild しなきゃいけなくなった場合もまたライブラリのダウンロードをし直す必要がなくなるので何かと便利であります。
services の volumes と root の volumes 両方に書きます。 - ビルド一時ファイルなどを 名前なし volume としてマウント
node であればだいたい dist というディレクトリが作成されてそこに成果物や、開発のために build されたものが一時ファイルとして格納されます。 理由としては前述したライブラリファイル群と同じく、直接いじることもなければコミットする必要もなく、コンテナ内にさえあれば良いファイルたちなので volume に設定します。 named である必要がないのは、ライブラリファイル群とは違い、基本的に開発環境を立ち上げるたびに再 build されるものだと思うので保持する必要がないからです。利用しているフレームワークや言語に合わせて named にするかどうかは決めたほうがいいかもしれません。 Blitz.js の場合ビルドファイルは.blitz
を生成してそこに格納されるので このように volume に設定します。 こちらは named と違って root の volumes には設定が不要です。
- リポジトリ自体のマウント
Dockerfile
公式リファレンスは こちら
-
FROM
とても奥が深いマルチステージビルドのための設定項目。詳しくは割愛しますが、Rails で webpacker を利用する場合は ruby だけではなく node も同時に必要だったりするので base image は ruby だけども node イメージから node の実行ファイルだけ取得するといったことをやったりします。
RUN を使って自前でインストールするよりも他 image から持ってくるほうが早くて確実という場合に使うと良いかもしれません。 -
microsoft が用意した devcontainer 用 image
直接 Dockerfile 関係ないのですが専用に用意された docker image があるのでこちらを指定して devcontainer を構築すると良いかもしれません。デフォルトで root ではないユーザでコンテナの実行を行ったり、git と連携された PS1 になっていたりいろいろな機能が備わっています。
なお、タグの指定ですが node14 の場合javascript-node:14
と指定をしたくなるのですが、なぜか最新バージョンのものを取得しなかったりするので、明示的に vscode-dev-containers のバージョンをを含めたjavascript-node:0-14
このように0-
を含めたほうが無難みたいです。
VSCode
- Remote Explorer
VSCode 左側メニューのディスプレイっぽいアイコンを選択するとコンテナの状況などが見られたりします。devcontainer の場合メインのコンテナ一つのコンソールを操作してそこで作業をしますが、時には別で立ち上げたコンテナのログとかを見たい場合があります。そんなときにはここから対象のコンテナを右クリックで選択しShow Container Log
でログを確認したり、Attach to Container
で実際にコンソールを取得して操作したりすることができます。
※この記事の内容は一部 WESEEK Tips wiki に 2020/12/13 に投稿された記事の転載です。
Tips wiki では、IT企業の技術的な情報やプロジェクトの情報を公開可能な範囲で公開してます。