🐳

VS Code 開発コンテナー用カスタム Docker イメージの開発方法

2024/01/07に公開

はじめに

ここでは VS Code 開発コンテナー用カスタム Docker イメージの開発方法について説明します。

動作確認は Linux 環境を使っているため、Windows や macOS とは違っている場合があります。できるだけ Docker が動作する環境であれば動くようにはしています。

この記事の対象者の前提は下記となります。

  • Visual Studio Code(VS Code)が使えること
  • Docker が使えること
  • Node.js で NPM を使ったパッケージ管理について基本的なことを知っていること
  • Docker Compose の基本的な使い方を知っていること
  • Linux の基本的な使い方を知っていること

使用する環境は下記です。

Docker については、Docker Desktop であれば、Docker Engine と Docker Compose が同梱されているので、それを使えば大丈夫です。

実際に作成したものは「hiro345g/devcon-node at 1.20」で公開していますので、よろしかったら、そちらも参考にしてください。

Docker イメージの管理

カスタム Docker イメージの作成について説明する前に、Docker イメージと、その管理方法について簡単に説明しておきます。

Docker イメージと Docker レジストリ

Docker イメージは、Docker コンテナー専用の起動ディスクイメージのようなもので、読み取り専用のテンプレートデータだと考えておけば良いです。Docker のエコシステムでは、Docker レジストリ(registry)という、Docker イメージを保存して配布するためのシステムが用意されていて、基本的にはそれを使って管理します。

Docker レジストリには、次のような種類があり、Docker を初めて使う人はデフォルトのレジストリとなっている Docker Hub を使うことが多いはずです。

種類 説明
パブリックレジストリ インターネット上で公開されていてユーザー制限がない公共用途のレジストリ
プライベートレジストリ LAN 環境に用意され、利用可能なユーザーが制限されているなど、アクセス元を制限した専用レジストリ
クラウドサービスのレジストリ 主にビジネス用途を想定したパブリッククラウドのサービスとして使用可能なレジストリ

パブリックレジストリとクラウドサービスのレジストリの分類はしにくいですが、個人が無料で利用できるものか、ビジネス用途で有料で利用することを想定して利用するものなのかで個人的な見解で分けています。

各レジストリの例については、次のものがあります。

Docker HubGitHub Packages では、世界中の開発者が Docker イメージを登録していて、それを検索したり、利用したりすることができるようになっています。ビジネス的に自社でパブリックレジストリのクラウドサービスを構築できる会社は、自社が提供する Docker イメージを Docker Hub ではなく自社のパブリックレジストリで提供していることが多いので、検索したり、利用したりするときは、そちらを使ったほうが良いことがあります。

ローカルマシンでの Docker イメージの管理

さて、Docker イメージの管理をするには、Docker レジストリを使うと説明をしましたが、ローカルマシンで Docker イメージを管理するにあたって、Docker レジストリがないと出来ないとなると不便です。Docker がそこそこ使えるようになるとプライベートレジストリを構築して管理できるようになりますが、そこまでしないといけないとなると Docker 利用のハードルが高くなってしまいます。

ということで、Docker ではローカルマシンに Docker イメージをキャッシュする仕組みが提供されています。ローカルマシンでは、このキャッシュを管理することで Docker イメージの管理をすることになります。

ローカルマシンでは、Docker レジストリで公開されているものを取得することができ、ローカルマシンにある Docker イメージには名前(タグ)をつけて利用することができます。また、新しいイメージをビルドして作ることもできます。

自作した Docker イメージを他のマシンで使ったり、他の人と共有することができるようにするためには、その Docker イメージを Docker レジストリへ登録します。登録にあたっては、Docker レジストリ用のアカウントが必要になります。

個人で Docker を使う場合は、Docker レジストリを使って Docker イメージを共有する必要性はあまりないので、ローカルマシンで Docker イメージを管理することが多くなります。ローカルマシンでビルドして自作したカスタム Docker イメージも、ローカルマシン内で使えれば良いという運用となるでしょう。

その場合は、ローカルマシンでカスタム Docker イメージをビルドできれば良いので、そのために必要な Dockerfile や Docker Compose ファイルをバージョン管理システムで管理して、Docker イメージ自体は破棄しても構わない状態で使うことが多くなります。

ということで、ここでは Docker Hub などの Docker レジストリで自作のカスタム Docker イメージを管理するところまでは含めずに説明します。ただし、Dockerfile や Docker Compose ファイルをバージョン管理システムで管理することは考慮します。

カスタム Docker イメージ devcon-node の作成

ここでは、カスタム Docker イメージとして devcon-node というものを作成するとして説明します。このイメージは「Node.js の開発ができて、docker コマンドと git コマンドが使えて、コンテナーが使うネットワークの確認ができるようなもの」にします。抽象的な用語での説明だとわかりにくくなるので、作るものに名前をつけて具体的に説明します。

カスタム Docker イメージを作成する方法としては大きく分けて、次の2つの方法があります。

  • 使っている Docker コンテナーから作成
  • Docker イメージビルド用のファイルから作成

使っている Docker コンテナーから作成するには、VS Code の Docker 拡張機能を利用するのが簡単です。

まず、CONTAINERS に表示されているコンテナーから Docker イメージを作成したいものを選びます。次に、そのイメージを IMAGES にある一覧から選択して、その Docker イメージに好きな名前でタグをつけるだけです。

タグにつけられる文字列には制限がありますが、他の Docker イメージと同様にして、「イメージ名:バージョン番号」とか「イメージ名:種類がわかる文字列」といったものにしておけば良いでしょう。区切り文字には「-」が使えます。

使っている Docker コンテナーから作成する方法は、手軽で動作しているものから作れるので初心者には良いのですが、再現性が低くなるという点に注意が必要です。Docker を使っていると、ローカルマシンのディスク容量が圧迫されて、Docker イメージのキャッシュをクリアしたくなるときがあります。そういったときに、この Docker イメージは消したら再現ができないとなると、管理が少し大変になります。

また、他の人に自作の Docker イメージを使ってもらいたいとなったときに、Docker コンテナーを用意して環境構築してから Docker イメージのタグをつけるという手作業をしてもらうことになります。これでは手軽に使ってもらうということができません。

ということで、Docker に使い慣れてくると、カスタム Docker イメージを使うなら、Docker イメージビルド用のファイルから作成する方が楽になってきます。

ここで、Docker イメージをビルドする方法にはいくつかあるのですが、VS Code と Docker 拡張機能が対応している範囲で作業をするのが簡単なので、その方法だけ説明します。

また、ここで想定しているカスタム Docker イメージの作成は、試しに利用してみるという一回限りのカスタム Docker イメージ作成ではなく、ローカルマシンでよく利用する Docker イメージの作成を想定するとします。

基本的に開発コンテナーについては、カスタム Docker イメージをビルドするための設定ファイルと、カスタム Docker イメージを利用する設定ファイルとは別に管理した方がわかりやすくなります。これは、カスタム Docker イメージを利用する場合にビルドのための設定ファイルやビルド環境は必要ないからです。

ここでは次の順番でカスタム Docker イメージを作成する方法の説明をします。

  1. devcon-node イメージの作成について
  2. 開発環境 devcon-node-build-dev
  3. devcon-node-build-dev 用設定ファイルの用意
  4. devcon-node をビルドするための環境
  5. devcon-node のベースイメージのビルド
  6. devcon-node ビルド用ファイルの作成
  7. devcon-node のビルド

devcon-node イメージの作成について

カスタム Docker イメージの作成について、一般的なコンテナー用の方法について説明すると長くなるので、ここでは開発コンテナー用のものに絞ります。手順としては、次のようになります。

  1. カスタム Docker イメージのビルド用設定ファイルを開発
  2. 開発したビルド用設定ファイルでカスタム Docker イメージをビルド

まず、1. の作業をするための開発環境が必要になります。そのために、devcon-node-build-dev コンテナーというものを用意します。つまり、最初に devcon-node-build-dev 用の Docker Compose ファイルを用意します。devcon-node-build-dev コンテナーは、カスタム Docker イメージのベースとなる Docker イメージを使った開発コンテナーにします。

そのようにして用意した devcon-node-build-dev コンテナーを実行して、そこで実際にカスタマイズのための処理を実行してみることで、カスタム Docker イメージのビルド用の Dockerfile が作れるようになります。また、カスタム Docker イメージ用のベースとなる Docker イメージのキャッシュもローカルマシンに用意されます。

次に、1. の作業の成果物を使って、カスタム Docker イメージをビルドします。これをビルドするには Node.js プログラムの @devcontainers/cli というものを使います。

開発環境 devcon-node-build-dev

devcon-node というカスタム Docker イメージを作成するにあたり、まずは開発環境 devcon-node-build-dev を用意すると説明をしました。ということで、次のようなディレクトリー構成で devcon-node-build-dev 用のファイルを用意します。

devcon-node/
├── build_devcon/
│   └── devcon-node-build-dev/
│       ├── .devcontainer/
│       │   └── devcontainer.json
│       └── docker-compose.yml
└── workspace_share/

最終的には devcon-node イメージをビルドするので、それ用に devcon-node/build_devcon というディレクトリーを用意し、その中に devcon-node-build-dev ディレクトリーを用意します。また、開発やビルド作業をするときに Docker ホストと Docker コンテナーとでファイルのやりとりをしやすくするために、devcon-node/workspace_share ディレクトリーも用意します。

なお、この記事で詳しい説明はしませんが、この後に紹介する devcontainer.jsondocker-compose.yml については、開発コンテナーのフィーチャーである docker-outside-of-docker というものを利用する前提のものになっています。そのため、少し複雑な指定になっています。

devcon-node-build-dev 用設定ファイルの用意

まず、image: にベースとなる開発コンテナー用の Docker イメージを指定した Docker Compose 用の設定ファイル docker-compose.yml を用意します。ここでは、devcontainers/images/src/typescript-node で公開されている mcr.microsoft.com/devcontainers/typescript-node:20-bookworm を使います。

ここでは次のようにしました。

docker-compose.yml
name: devcon-node
services:
  devcon-node-build-dev:
    image: mcr.microsoft.com/devcontainers/typescript-node:20-bookworm
    container_name: devcon-node-build-dev
    hostname: devcon-node-build-dev
    init: true
    tty: true
    volumes:
      - type: bind
        source: ${SHARE_DIR:-../../workspace_share}
        target: /share

また、開発コンテナーとして使うので、.devcontainer ディレクトリーに devcontainer.json を用意します。

devcontainer.json には使用する docker-compose.yml とサービス名を指定します。"dockerComposeFile": を指定した場合は "workspaceFolder": の指定も必要なのでしています。さらに、使いたい feature 機能を devcontainers/features から選択して指定します。ここでは、カスタム Docker イメージをビルドする時に必要な情報だけ指定します。

ここでは次のようにしました。

devcontainer.json
{
  "name": "devcon-node-build-dev",
  "dockerComposeFile": [
    "../docker-compose.yml"
  ],
  "service": "devcon-node-build-dev",
  "workspaceFolder": "/home/node",
  "features": {
    "ghcr.io/devcontainers/features/docker-outside-of-docker:1.3.1": {
      "version": "latest",
      "dockerDashComposeVersion": "v2"
    },
    "ghcr.io/devcontainers/features/git:1.1.6": { "version": "latest" },
    "ghcr.io/devcontainers/features/git-lfs:1.1.1": { "version": "latest" }
  }
}

このようにファイルを用意してから VS Code で devcon-node/build_devcon/devcon-node-build-dev を開くと、「コンテナーで再度開く(英語だと Reopen in Container)」の通知が表示されるので、クリックして開発コンテナーとして使用します。すると、VS Code が開発コンテナーへ接続する処理を始めます。VS Code が開発コンテナーに接続すると、VS Code をアタッチした開発コンテナーの画面になります。VS Code が開発コンテナーに接続していることは、VS Code の左下の表示を確認するとわかります。

作業をするときは、VS Code をアタッチした開発コンテナー(devcon-node-build-dev コンテナー)のエディタ画面やターミナル画面を使います。ターミナルを開くと、mcr.microsoft.com/devcontainers/typescript-node:20-bookworm ではプロンプトに Oh My Posh ベースのものが使われているので、node ➜ ~ $ というプロンプトになります。

devcon-node ビルド用設定ファイルの開発

それでは、ビルド用設定ファイルの開発方法について説明します。といっても開発時は試行錯誤があるものです。ここでは、devcon-node コンテナーを用意するにあたって、どのようなことをしたのか、具体例を示した方がイメージがしやすいでしょう。

ここでは /home/node/.bashrc をカスタマイズして使いたかったので、これを cp コマンドで Docker ホストの devcon-node/workspace_share ディレクトリーにバインドマウントされている /share/node.dot.bashrc ディレクトリーへコピーしました。これで、Docker ホスト側のターミナルや VS Code で devcon-node/workspace_share/node.dot.bashrc ファイルが使えるようになります。

node ➜ ~ $ cp .bashrc /share/node.dot.bashrc

Docker ホスト側のターミナルを使って同じようにファイルコピーしたい場合は docker compose cp を使います。ここでは、Docker Compose のプロジェクト名が devcon-node-build-dev となるので、これを -p を使って指定します。具体的なコマンドは次のようになります。

docker compose -p devcon-node-build-dev \
  cp devcon-node-build-dev:/home/node/.bashrc node.dot.bashrc

これでカレントディレクトリーに devcon-node/workspace_share/.bashrc ファイルがコピーされます。

実際は devcon-node-build-dev コンテナーの中で試行錯誤して初期化の方法を決めましたが、スクリプトでまとめると次の init.sh のような処理になりました。

init.sh
#!/bin/sh

if [ -e /share/node.dot.bashrc ]; then
    cp /share/node.dot.bashrc /home/node/.bashrc
fi
if [ ! -e /home/node/.npmrc ]; then
    if [ -e /share/node.dot.npmrc ]; then
        cp /share/node.dot.npmrc /home/node/.npmrc
    fi
fi

apt-get update && apt-get -y upgrade \
    && export DEBIAN_FRONTEND=noninteractive \
    && apt-get -y install bash-completion \
        iproute2 iputils-ping dnsutils \
    && sed -i -E 's/# (ja_JP.UTF-8)/\1/' /etc/locale.gen \
    && locale-gen \
    && rm /etc/localtime \
    && ln -s /usr/share/zoneinfo/Asia/Tokyo /etc/localtime \
    && mkdir -p /home/node/workspace /home/node/.vscode-server/extensions \
    && cp -r /usr/local/share/npm-global/ /home/node/workspace/.npm-global \
    && chown -R node:node \
            /home/node/.bashrc /home/node/.npmrc \
            /home/node/workspace /home/node/.vscode-server \
            /home/node/workspace/.npm-global

node.dot.bashrcnode.dot.npmrc は、カスタム Docker イメージをビルドするときに使うので、最終的には Docker ホスト側にあった方が良いです。そのため、devcon-node-build-dev コンテナーの /share ディレクトリーに置いてあるものを使うようにしてあります。

なお、これらのファイルを作成するにあたっては、改行コードや文字コードで問題が起きないように、VS Code をアタッチした devcon-node-build-dev コンテナーの方で作成するのが良いです。

このようにスクリプトを用意して、devcon-node-build-dev コンテナーの中で動作することを確認しておけば、カスタム Docker イメージ用の Dockerfile を使ったビルドの確認がしやすくなります。

devcon-node をビルドするための環境

ビルド用設定ファイルが開発できたら、ビルドするための環境を用意します。ビルドに必要なファイルは devcon-node/build_devcon へまとめます。ディレクトリー構造は次のようになります。

devcon-node/
├── build_devcon/
│   ├── .devcontainer/
│   │   ├── Dockerfile
│   │   ├── devcontainer.json
│   │   ├── node.dot.bashrc
│   │   └── node.dot.npmrc
│   ├── build-base.sh
│   ├── build.sh
│   └── devcon-node-build-dev/(略)
└── workspace_share/

ビルド用設定ファイルは devcon-node/build_devcon/.devcontainer ディレクトリーに置きます。また、このディレクトリーにある設定ファイルを使ってビルドするスクリプトを devcon-node/build_devcon/build.sh に用意します。

devcon-node のベースイメージのビルド

まず、devcon-node のベースイメージを用意します。devcon-node-build-dev コンテナーを使って、devcon-node 用 Dockerfile で実行する処理の動作確認をしたので、devcon-node-build-dev コンテナー用のイメージを用意して、それをベースイメージとして Dockerfile で利用するのが合理的です。devcon-node/build_devcon/devcon-node-build-dev に設定ファイルがあるので、これを使います。

devcon-node のベースイメージの名前は、devcon-node-build-dev:1.20 などとしても良いのですが、開発環境用のイメージと間違えてもいけないので、devcon-node-base:1.20 とします。作成するイメージのバージョンは 1 で、このイメージに含まれる Node.js のバージョンが 20 なので、それがわかるように 1.20 とします。

devcontainers/images/src/typescript-node をベースイメージとする開発コンテナーのビルドには Node.js プログラムの @devcontainers/cli パッケージに含まれる devcontainer build コマンドが使えるので、次のようなビルド用スクリプトを用意することができます。

build-base.sh
#! /bin/sh
IMAGE_NAME=devcon-node-base:1.20
BUILD_DEVCON_DIR=$(cd $(dirname $0);pwd)
PATH=${PATH}:${NPM_CONFIG_PREFIX}/bin

cd ${BUILD_DEVCON_DIR}
npm exec --package=@devcontainers/cli -- \
    devcontainer build --workspace-folder ./devcon-node-build-dev --image-name ${IMAGE_NAME} --no-cache

このスクリプトについての詳しい説明はしませんが、IMAGE_NAME=devcon-node-base:1.20 で作成するカスタム Docker イメージの名前を指定しています。そのため、このスクリプトを実行することで devcon-node-base:1.20 という Docker イメージが用意されます。

また、ここでは --no-cache を指定することで、ローカルホストにあるキャッシュを使わずに、Docker レジストリから最新の Docker イメージを取得してビルドがされます。--no-cache を指定しない場合は、ローカルホストにあるキャッシュが使われます。この違いは理解しておいてください。

さて、Docker ホストが Linux で、Node.js の環境がある場合は、このスクリプトを実行すれば良いのですが、ない場合でも大丈夫です。開発コンテナーである devcon-node-build-dev コンテナーがビルドに使えます。これは、devcon-node-build-dev コンテナーには docker-outside-of-docker フィーチャーの機能が含まれていて、Docker ホストで動作している dockerd を利用できるようになっているからです。

VS Code をアタッチした devcon-node-build-dev コンテナーを動かしたら、Docker ホスト側のターミナルで、devcon-node をカレントディレクトリーとして、次のコマンドで、build_devcon を devcon-node-build-dev コンテナーの /home/node/ へコピーします。

docker compose -p devcon-node-build-dev \
  cp build_devcon devcon-node-build-dev:/home/node/

次に VS Code をアタッチした devcon-node-build-dev コンテナーでビルドスクリプトを実行します。

node ➜ ~ $ sh /home/node/build_devcon/build-base.sh 
(略)
{"outcome":"success","imageName":["devcon-node-base:1.20"]}
npm notice 
npm notice New major version of npm available! 9.8.1 -> 10.2.5
npm notice Changelog: https://github.com/npm/cli/releases/tag/v10.2.5
npm notice Run npm install -g npm@10.2.5 to update!
npm notice 

実行結果で、{"outcome":"success","imageName":["devcon-node-base:1.20"]} のように出力されていることから、devcon-node-base:1.20 の Docker イメージが作成できたことがわかります。

なお、ここで Run npm install -g npm@10.2.5 to update! と出力がされていることから、devcon-node-build-dev コンテナーにインストールされている npm コマンドのバージョンが古いということがわかります。これはつまり、devcon-node-base:1.20 にインストールされている npm コマンドのバージョンが古いということになります。

少しややこしく感じるかもしれませんが、devcon-node-build-dev コンテナーは devcon-node-base:1.20 のイメージを作るときに使った devcon-node/build_devcon/devcon-node-build-dev ディレクトリーの設定ファイルを使って起動していることを思い出してください。

カスタム Docker イメージのベースイメージが作れたので、次の作業に進みます。

devcon-node ビルド用ファイルの作成

次に、カスタム Docker イメージのビルド用ファイルを用意します。カスタム Docker イメージのベースイメージは devcon-node-base:1.20 となるので、これを FROM に指定します。

後は動作確認が出来ている init.sh をベースにして Dockerfile を用意します。ここで、カスタム Docker イメージのベースイメージ作成時に判明した npm コマンドのバージョンが古いことへの対策としては、メッセージにあった npm install -g npm@10.2.5 というコマンドを実行することで対応ができます。devcon-node-build-dev コンテナーで、このコマンドが実行できることを確認して反映します。

init.sh でコピーしていたファイルの node.dot.bashrcnode.dot.npmrc は、Dockerfile では COPY コマンドで Docker イメージにコピーするようにします。また、これらのファイルは Dockerfile と同じディレクトリーに用意します。

以上の結果、Dockerfile は次のようになりました。

Dockerfile
FROM devcon-node-base:1.20

COPY ./node.dot.bashrc /home/node/.bashrc
COPY ./node.dot.npmrc /home/node/.npmrc

RUN npm install -g npm@10.2.5 \
    && apt-get update && apt-get -y upgrade \
    && export DEBIAN_FRONTEND=noninteractive \
    && apt-get -y install bash-completion \
        iproute2 iputils-ping dnsutils \
    && sed -i -E 's/# (ja_JP.UTF-8)/\1/' /etc/locale.gen \
    && locale-gen \
    && rm /etc/localtime \
    && ln -s /usr/share/zoneinfo/Asia/Tokyo /etc/localtime \
    && mkdir -p /home/node/workspace /home/node/.vscode-server/extensions \
    && cp -r /usr/local/share/npm-global/ /home/node/workspace/.npm-global \
    && chown -R node:node \
            /home/node/.bashrc /home/node/.npmrc \
            /home/node/workspace /home/node/.vscode-server \
            /home/node/workspace/.npm-global

この Dockerfile ファイルを使う devcontainer.json は次のように単純なものになります。

{
  "name": "devcon-node-build",
  "dockerFile": "./Dockerfile"
}

ビルドするスクリプト build.sh は次のようになります。

build.sh
#! /bin/sh
IMAGE_NAME=devcon-node:1.20
BUILD_DEVCON_DIR=$(cd $(dirname $0);pwd)
PATH=${PATH}:${NPM_CONFIG_PREFIX}/bin

cd ${BUILD_DEVCON_DIR}
npm exec --package=@devcontainers/cli -- \
    devcontainer build --workspace-folder ./ --image-name ${IMAGE_NAME}

IMAGE_NAME=devcon-node:1.20 で作成する Docker イメージ名を指定し、ビルドするときに build.sh と同じディレクトリーにある .devcontainer ディレクトリーの設定ファイルを使うようにしています。また、ここでは、ローカルホストにあるキャッシュのイメージ devcon-node:1.20 を使うため、--no-cache の指定はしていません。

devcon-node のビルド

用意が出来たら、カスタム Docker イメージのビルドをします。devcon-node-base:1.20 を作った時と同じですが、念の為にもう一度説明します。

VS Code をアタッチした devcon-node-build-dev コンテナーを動かしたら、Docker ホスト側のターミナルで、devcon-node をカレントディレクトリーとして、次のコマンドで、build_devcon を devcon-node-build-dev コンテナーの /home/node/ へコピーします。

docker compose -p devcon-node-build-dev \
  cp build_devcon devcon-node-build-dev:/home/node/

次に VS Code をアタッチした devcon-node-build-dev コンテナーでビルドスクリプト build.sh を実行します。

node ➜ ~ $ sh /home/node/build_devcon/build.sh 
(略)
{"outcome":"success","imageName":["devcon-node:1.20"]}

実行結果で、{"outcome":"success","imageName":["devcon-node:1.20"]} と出力されたら成功です。

devcon-node イメージの利用

作成した devcon-node イメージを利用するために、次のようなディレクトリー構成でファイルを用意します。build_devcon ディレクトリーについて、ここでは説明をするときのファイル一式ということで一緒に含めてありますが、devcon-node イメージの利用時にはなくても大丈夫です。

devcon-node/
├── .devcontainer/
│   └── devcontainer.json
├── build_devcon/(略)
├── docker-compose.yml
├── sample.env
└── workspace_share/

利用時に使う devcontainer.json には、Node.js の開発時に便利な設定や拡張機能を指定します。また、カスタマイズ Docker イメージを作るまでもないけど利用時にカスタマイズしたいようなものは docker-compose.yml を使います。

ここでは、次のような内容にしました。

devcon-node/.devcontainer/devcontainer.json
{
  "name": "devcon-node",
  "dockerComposeFile": [
    "../docker-compose.yml"
  ],
  "service": "devcon-node",
  "workspaceFolder": "/home/node/workspace",
  "shutdownAction": "none",
  "customizations": {
    "vscode": {
      "settings": {
        "terminal.integrated.defaultProfile.linux": "bash",
        "editor.formatOnSave": true,
        "editor.tabSize": 4,
        "editor.detectIndentation": false,
        "editor.insertSpaces": true,
        "editor.defaultFormatter": "esbenp.prettier-vscode"
      },
      "extensions": [
        "esbenp.prettier-vscode",
        "oderwat.indent-rainbow",
        "eamodio.gitlens",
        "donjayamanne.githistory",
        "mhutchie.git-graph",
        "ms-azuretools.vscode-docker",
        "christian-kohler.npm-intellisense"
      ]
    }
  }
}

docker-compose.yml では Docker ネットワークや Docker ボリュームなど、利用するリソースについての設定をしています。また、Docker コンテナーを操作しやすくするための設定などもしています。

devcon-node/docker-compose.yml
name: devcon-node
services:
  devcon-node:
    image: devcon-node:1.0
    container_name: devcon-node
    hostname: devcon-node
    init: true
    tty: true
    user: node
    working_dir: /home/node/workspace
    networks:
      devcon-node-net:
    volumes:
      - workspace-data:/home/node/workspace
      - vscode-server-extensions:/home/node/.vscode-server/extensions
      - type: bind
        source: ${SHARE_DIR:-./workspace_share}
        target: /share
    environment:
      ENV LANGUAGE: ja_JP.UTF-8
      ENV LANG: ja_JP.UTF-8
      ENV LC_ALL: ja_JP.UTF-8
      EDITOR: code
      NPM_CONFIG_USERCONFIG: ${NPM_CONFIG_USERCONFIG:-/home/node/.npmrc}
      DH_SHARE_DIR: ${SHARE_DIR:-./workspace_share}

volumes:
  workspace-data:
    name: devcon-node-node-workspace-data
  vscode-server-extensions:
    name: devcon-node-vscode-server-extensions

networks:
  devcon-node-net:
    name: devcon-node-net

ここで、 docker-outside-of-docker のフィーチャーを使う時に、バインドマウントを含む Docker Compose 設定ファイルは扱いにくいので、開発コンテナーでは、できるだけ使わないようにしています。とはいえ、バインドマウントがあった方が便利な場面はあるので、devcon-node:/share を Docker ホストの workspace_share ディレクトリーにバインドしています。

これで、devcon-node ディレクトリーを VS Code で開くと、カスタム Docker イメージによる開発コンテナーの起動ができるようになります。

開発コンテナーは、実行用コンテナーとは違って、使いまわしをしたいことが多いので、利用時に使う docker-compose.yml でカスタマイズしたい部分と、Dockerfile で Docker イメージ自体をカスタマイズしたい部分と、それぞれをきちんと分けて設計する必要があります。

今回は、自分にとって使いやすいカスタム Docker イメージとして「Node.js の開発ができて、docker コマンドと git コマンドが使えて、コンテナーが使うネットワークの確認ができるようなもの」を用意しました。利用時は、React アプリを開発する時、Angular アプリを開発する時、Express アプリを開発するときで使用する docker-compose.ymldevcontainer.json が変わってきますが、Docker イメージについては devcon-node が共通で使えます。

手順が複雑に感じた部分もあると思いますが、各ステップでやっていることが理解できれば、開発コンテナーのカスタマイズについて整理がしやすくなるはずです。また、一度ビルドができてしまえば、後は各ステップでの設定ファイルを変更するだけで、自分好みの開発コンテナー用 Docker イメージが作れるはずです。

ぜひ、便利な開発コンテナー用 Docker イメージの作成と、それを利用する docker-compose.yml ファイルを作成してみてください。

Discussion