オリジナルの Dev Container のコンテナイメージを作成してみる
みなさん、Dev Container を活用していますか?
ローカルの環境を汚さずに、開発環境を構築して開発を行うことができる Dev Container はとても便利です。
また、コードベースで開発環境を共有することができるため、チーム開発においても非常に有用です。
この記事では、オリジナルの Dev Container のコンテナイメージの作成を通して、Dev Container の基本的な使い方から、カスタマイズ、GitHub Container Registry との連携まで解説します。
Dev Container について
Dev Container とはコンテナを開発環境として利用するための仕組みです。
devcontainer.json
というファイルに、コンテナの構成情報を記述することで、Visual Studio Code などのエディタからコンテナを起動し、コンテナ内で開発を行うことができます。
もともとは、Visual Studio Code の拡張機能「Remote - Containers」で提供されていた機能ですが、現在では独立したOSSとして提供されています。
Visual Studio Code 以外にも、IntelliJ IDEA / GitHub Codespaces / CodeSandbox などでも利用できます。
Dev Container の使い方
まず、適当なディレクトリを作成し、そちらで Dev Container の設定を行います。
mkdir devcontainer
Visual Studio Code を使う場合は、そのディレクトリを開きます。
code devcontainer
.devcontainer/devcontainer.json
ファイルを作成し、以下のように記述します。
{
"name": "Node.js",
"image": "mcr.microsoft.com/devcontainers/javascript-node:20"
}
設定したコンテナイメージは Dev Container 用に作られたコンテナイメージになります。
Visual Studio Code の場合、Docker Desktop などの Docker 環境をインストールして、Visual Studio Code の拡張機能「Dev Containers」をインストールします。
コマンドパレットを開いて「Dev Containers: Reopen in Container」を選択すると、Dev Container が起動します。
Dev Container の起動後、ターミナルを開くとコンテナ内で作業していることがわかります。
devcontainer.json
ファイルには、さまざまな設定を記述できます。
たとえば、postCreateCommand
プロパティを使って、コンテナ作成時に実行するコマンドを指定できます。
また、customizations
プロパティを使って、各エディタのカスタマイズも可能です。
{
"name": "Node.js",
"image": "mcr.microsoft.com/devcontainers/javascript-node:20",
"postCreateCommand": "npm install",
"customizations": {
"vscode": {
"settings": {
"terminal.integrated.defaultProfile.linux": "zsh"
},
"extensions": [
"eamodio.gitlens",
"EditorConfig.EditorConfig",
"mhutchie.git-graph",
"mikestead.dotenv"
]
}
}
}
コンテナイメージをカスタマイズ
既存の Dev Container 用のコンテナイメージをカスタマイズすることもできます。
先ほどのコンテナイメージでは、less
がインストールされていないため、less
をインストールする Dockerfile を作成してみます。
.devcontainer/Dockerfile
ファイルを作成し、以下のように記述します。
FROM mcr.microsoft.com/devcontainers/javascript-node:20
RUN apt-get update && apt-get -y install --no-install-recommends \
less
{
"name": "Dev Container",
"build": {
"dockerfile": "Dockerfile"
}
}
上記のように、Dockerfile を作成し、devcontainer.json
に build
プロパティを設定することで、Dockerfile から Dev Container のコンテナイメージを作成できます。
コンテナイメージ作成
Dev Container のコンテナイメージは特別なものではなく、Dockerfile からビルドされたコンテナイメージです。
そのため、Dockerfile を作成し、ベースイメージから Dev Container のコンテナイメージを作成することもできます。
以下に、Debian をベースイメージとして Dev Container のコンテナイメージを作成してみます。
Dockerfileの作成
.devcontainer/Dockerfile
ファイルを作成し、以下のように記述します。
FROM debian:bookworm
ARG USERNAME=devuser
ARG USER_UID=1000
ARG USER_GID=$USER_UID
ARG LOCALE=ja_JP.UTF-8
ARG TIME_ZONE=Asia/Tokyo
ENV LANGUAGE=${LOCALE}
ENV LC_ALL=${LOCALE}
ENV TZ=${TIME_ZONE}
ENV DEBIAN_FRONTEND=noninteractive
# 開発に必要なツールをインストール
RUN apt-get update && apt-get upgrade -y && apt-get install -y --no-install-recommends \
build-essential \
ca-certificates \
chromium \
curl \
dnsutils \
fonts-ipafont \
git \
gnupg \
imagemagick \
jq \
less \
libssl-dev \
locales \
mariadb-client \
ncdu \
openssh-client \
postgresql-client \
rsync \
shellcheck \
sqlite3 \
sudo \
tree \
unzip \
wget \
vim \
yq \
zip \
zsh \
# clean up
&& apt-get autoremove -y \
&& apt-get clean -y \
&& rm -rf /var/lib/apt/lists/*
# ロケールの設定 / ユーザーの追加 / 作業ディレクトリの作成
RUN : \
# locale
&& sed -i -E "s/# (${LOCALE})/\1/" /etc/locale.gen \
&& locale-gen ${LOCALE} \
# user
&& groupadd --gid ${USER_GID} ${USERNAME} \
&& useradd -s /bin/bash --uid ${USER_UID} --gid ${USER_GID} -m ${USERNAME} \
&& echo ${USERNAME} ALL=\(root\) NOPASSWD:ALL > /etc/sudoers.d/${USERNAME} \
&& chmod 0440 /etc/sudoers.d/${USERNAME} \
# work directory
&& mkdir -p /workspace \
&& chown ${USERNAME}:${USERNAME} /workspace
# Oh My Zsh のインストール / zsh のデフォルトシェル設定
RUN su ${USERNAME} -c 'sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" "" --unattended' \
&& chsh -s /usr/bin/zsh ${USERNAME}
# Oh My Zsh のプラグインをインストール
ENV OH_MY_ZSH_DIR=/home/${USERNAME}/.oh-my-zsh
RUN : \
&& git clone https://github.com/zsh-users/zsh-autosuggestions ${OH_MY_ZSH_DIR}/custom/plugins/zsh-autosuggestions \
&& git clone https://github.com/zsh-users/zsh-syntax-highlighting.git ${OH_MY_ZSH_DIR}/custom/plugins/zsh-syntax-highlighting \
&& git clone https://github.com/zsh-users/zsh-completions ${OH_MY_ZSH_DIR}/custom/plugins/zsh-completions \
&& git clone https://github.com/marlonrichert/zsh-autocomplete.git ${OH_MY_ZSH_DIR}/custom/plugins/zsh-autocomplete \
&& chown -R ${USERNAME}:${USERNAME} ${OH_MY_ZSH_DIR}/custom/plugins
# zsh の設定
RUN echo '' > /home/${USERNAME}/.zshrc \
&& echo 'export ZSH=~/.oh-my-zsh' >> /home/${USERNAME}/.zshrc \
&& echo 'ZSH_THEME="robbyrussell"' >> /home/${USERNAME}/.zshrc \
&& echo 'ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE="fg=#999999"' >> /home/${USERNAME}/.zshrc \
&& echo 'ZSH_AUTOSUGGEST_STRATEGY=(history completion)' >> /home/${USERNAME}/.zshrc \
&& echo 'plugins=(git docker composer zsh-syntax-highlighting zsh-autosuggestions zsh-completions zsh-autocomplete)' >> /home/${USERNAME}/.zshrc \
&& echo 'source $ZSH/oh-my-zsh.sh' >> /home/${USERNAME}/.zshrc \
&& echo 'autoload -U compinit && compinit' >> /home/${USERNAME}/.zshrc \
&& echo 'zstyle ":completion:*" matcher-list "m:{a-zA-Z}={A-Za-z}"' >> /home/${USERNAME}/.zshrc \
&& echo 'setopt HIST_IGNORE_DUPS' >> /home/${USERNAME}/.zshrc \
&& echo 'setopt HIST_IGNORE_SPACE' >> /home/${USERNAME}/.zshrc \
&& echo 'setopt HIST_REDUCE_BLANKS' >> /home/${USERNAME}/.zshrc \
&& echo 'setopt AUTO_CD' >> /home/${USERNAME}/.zshrc \
&& chown ${USERNAME}:${USERNAME} /home/${USERNAME}/.zshrc
# nvm のインストール / nvm の設定
ENV NVM_DIR=/home/${USERNAME}/.nvm
RUN mkdir -p ${NVM_DIR} \
&& curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.5/install.sh | bash \
&& chown -R ${USERNAME}:${USERNAME} ${NVM_DIR} \
# Add nvm configuration
&& echo 'export NVM_DIR="$HOME/.nvm"' >> /home/${USERNAME}/.zshrc \
&& echo '[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"' >> /home/${USERNAME}/.zshrc \
&& echo '[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"' >> /home/${USERNAME}/.zshrc
# 作業ディレクトリの設定
WORKDIR /workspace
# zsh の起動
CMD ["zsh"]
Dev Container として利用するために devcontainer.json
ファイルを作成し、以下のように記述します。
{
"name": "Example Dev Container",
"build": {
"dockerfile": "Dockerfile"
},
"remoteUser": "devuser",
"postCreateCommand": ". ~/.nvm/nvm.sh && nvm install 20 && nvm use 20",
}
Dev Container のコンテナイメージ作成の一例ですが、このように開発に必要なツールをインストールし、設定を行い、Dev Container として利用できます。
コンテナイメージのビルド
Dev Container 用に作成した Dockerfile ですが、通常の Dockerfile と同様にビルドして使うこともできます。
.devcontainer
ディレクトリ内にある Dockerfile をビルド
docker build -t example-dev-container .devcontainer
ビルドしたコンテナイメージを使ってコンテナを起動
docker run -it -u devuser --rm example-dev-container
ビルドしたコンテナイメージを使って Dev Container を起動することも可能です。
{
"name": "Example Dev Container",
"image": "example-dev-container",
"remoteUser": "devuser",
"postCreateCommand": ". ~/.nvm/nvm.sh && nvm install 20 && nvm use 20",
}
コンテナレジストリにプッシュ
ビルドしたコンテナイメージをコンテナレジストリにプッシュして、Dev Container として利用することもできます。
コンテナレジストリの Docker Hub にプッシュする例を以下に示します。
docker login -u [username]
docker tag example-dev-container:latest [username]/example-dev-container:latest
docker push [username]/example-dev-container:latest
Docker Hub にプッシュしたコンテナイメージを使用して、Dev Container を起動可能です。
{
"name": "Example Dev Container",
"image": "[username]/example-dev-container:latest",
"remoteUser": "devuser",
"postCreateCommand": ". ~/.nvm/nvm.sh && nvm install 20 && nvm use 20",
}
GitHub Actions / GitHub Container Registry
コンテナレジストリは Docker Hub 以外にも、Amazon Elastic Container Registry、Azure Container Registry、などさまざまなレジストリがあります。
GitHubでも、GitHub Container Registry が提供されており、GitHub Actions を利用して GitHub Container Registry にプッシュできます。
GitHub で Dev Container の Dockerfile を管理しつつ、GitHub Actions を利用して GitHub Container Registry にプッシュすることで、Dev Container のコンテナイメージを管理できます。
以下に、GitHub Actions を使って GitHub Container Registry にプッシュする例を示します。
GitHub のリポジトリに反映するための、ディレクトリを作成します。
mkdir example-dev-container
cd example-dev-container
Dev Container 用の Dockerfile
を作成します。
FROM debian:bookworm
# 以下省略
Dockerfile をビルドとプッシュを行う GitHub Actions のワークフローを作成します。
.github/workflows/publish.yml
ファイルを作成し、以下のように記述します。
name: Publish to Container Registry
on:
push:
tags:
- '*'
permissions:
packages: write
contents: read
security-events: write
env:
REGISTRY: ghcr.io
IMAGE_NAME: example-dev-container
VERSION: ${{ github.ref_name }}
jobs:
publish:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Generate metadata
id: metadata
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}
tags: |
type=raw,value=${{ env.VERSION }}
- name: Login to Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build container
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ${{ steps.metadata.outputs.tags }}
labels: ${{ steps.metadata.outputs.labels }}
Git でリポジトリを初期化しコミットします。
git init
git add .
git commit -m "first commit"
GitHub にリポジトリを作成し、リモートリポジトリにプッシュします。
gh auth login
gh repo create example-dev-container --public --source=. --push
tag を作成し、リモートリポジトリにプッシュします。
tag をリモートリポジトリにプッシュすることにより、 GitHub Actions のワークフローが実行され、コンテナイメージが GitHub Container Registry にプッシュされます。
git tag -a latest -m "latest"
git push origin latest
下記コマンドで、GitHub Actions の実行結果を確認できます。
実行が成功すると、GitHub Container Registry にコンテナイメージがプッシュされていることが確認できます。
gh run list
GitHub Container Registry にプッシュしたコンテナイメージを使用して、Dev Container を起動可能です。
{
"name": "Example Dev Container",
"image": "ghcr.io/[username]/example-dev-container:latest",
"remoteUser": "devuser",
"postCreateCommand": ". ~/.nvm/nvm.sh && nvm install 20 && nvm use 20",
}
コンテナイメージのメタデータ設定
コンテナイメージにはラベルを使用してメタデータを設定できます。
Dev Container では、ラベルの devcontainer.metadata
の値を読み込み設定を行います。
Microsoft が提供している Dev Container 用のコンテナイメージには、以下のようなメタデータが設定されています。
docker pull mcr.microsoft.com/devcontainers/javascript-node:20
docker inspect mcr.microsoft.com/devcontainers/javascript-node:20 | jq '.[0].Config.Labels'
{
"dev.containers.id": "javascript-node",
"dev.containers.release": "v0.4.8",
"dev.containers.source": "https://github.com/devcontainers/images",
"dev.containers.timestamp": "Wed, 16 Oct 2024 17:55:58 GMT",
"dev.containers.variant": "20-bookworm",
"devcontainer.metadata": "[ {\"id\":\"ghcr.io/devcontainers/features/common-utils:2\"}, {\"id\":\"ghcr.io/devcontainers/features/git:1\"}, {\"id\":\"ghcr.io/devcontainers/features/node:1\",\"customizations\":{\"vscode\":{\"extensions\":[\"dbaeumer.vscode-eslint\"]}}}, {\"customizations\":{\"vscode\":{\"extensions\":[\"dbaeumer.vscode-eslint\"]}},\"remoteUser\":\"node\"} ]",
"version": "1.1.6"
}
devcontainer.metadata
の内容を確認してみると、ghcr.io/devcontainers/features/git
などの features
が設定されていたり、
Visual Studio Code の dbaeumer.vscode-eslint
の拡張機能が設定されていたりします。
また、remoteUser
の設定が行われている関係で、devcontainer.json
に remoteUser
の設定をしなくとも、指定のユーザーでコンテナが起動されるようになっています。
メタデータの設定
GitHub Actions でコンテナイメージにメタデータを設定する方法を紹介します。
devcontainer.metadata
に設定する値の JSON ファイルを作成します。
devcontainer-metadata.json
ファイルを作成し、以下のように記述します。
{
"remoteUser": "devuser",
"postCreateCommand": ". ~/.nvm/nvm.sh && nvm install 20 && nvm use 20",
"customizations": {
"vscode": {
"settings": {
"terminal.integrated.defaultProfile.linux": "zsh",
"terminal.integrated.persistentSessionReviveProcess": "never"
},
"extensions": [
"eamodio.gitlens",
"EditorConfig.EditorConfig",
"mhutchie.git-graph",
"mikestead.dotenv"
]
}
}
}
GitHub Actions で devcontainer-metadata.json
ファイルを読み込み、コンテナイメージにメタデータを設定します。
name: Publish to Container Registry
on:
push:
tags:
- '*'
permissions:
packages: write
contents: read
security-events: write
env:
REGISTRY: ghcr.io
IMAGE_NAME: example-dev-container
VERSION: ${{ github.ref_name }}
jobs:
publish:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Read devcontainer metadata
id: devcontainer-metadata
run: |
if [ -f "devcontainer-metadata.json" ]; then
echo "exists=true" >> $GITHUB_OUTPUT
# エスケープが正しく行われない場合があるため、カンマにスペースを追加
echo "content=$(cat devcontainer-metadata.json | jq -c | sed 's/,/ , /g')" >> $GITHUB_OUTPUT
else
echo "exists=false" >> $GITHUB_OUTPUT
fi
- name: Generate metadata
id: metadata
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}
tags: |
type=raw,value=${{ env.VERSION }}
labels: |
${{ steps.devcontainer-metadata.outputs.exists == 'true' && format('devcontainer.metadata={0}', steps.devcontainer-metadata.outputs.content) || '' }}
- name: Login to Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build container
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ${{ steps.metadata.outputs.tags }}
labels: ${{ steps.metadata.outputs.labels }}
先ほどの例で GitHub でリポジトリを作成した方は、コミットしてタグを再プッシュしてください。
コミット
git add .
git commit -m "Dev Containerメタデータの設定"
git push
タグ削除
git tag -d latest
git push -d origin latest
タグ作成・プッシュ
git tag -a latest -m "latest"
git push origin latest
確認
gh run list
以下のコマンドで、GitHub Container Registry にプッシュしたコンテナイメージのメタデータを確認できます。
docker pull ghcr.io/[username]/example-dev-container:latest
docker inspect ghcr.io/[username]/example-dev-container:latest | jq '.[0].Config.Labels'
終わりに
この記事では、Dev Containerの基本から、オリジナルのコンテナイメージの作成、さらにGitHub Container Registryとの連携まで解説してきました。
Dev Containerを活用することで、以下のようなメリットが得られます。
- プロジェクトごとに独立した開発環境を簡単に構築できる
- チーム全体で同じ開発環境を共有でき、「自分の環境では動くのに…」という問題を解消できる
- 新しいメンバーが参加した際も、すぐに開発環境を立ち上げることができる
また、この記事で紹介したように、Dev Containerをカスタマイズすることで、プロジェクトやチームの要件に合わせた最適な開発環境を構築できます。
Dev Container は、モダンな開発環境の構築に欠かせないツールの1つとなっています。
ぜひ、この記事を参考に、みなさんのプロジェクトでも Dev Container を活用してみてください。
開発効率の向上とチームメンバー間の環境差異の解消に、大きく貢献してくれるはずです。
最後に、この記事で紹介したサンプルコードは、GitHubリポジトリで公開していますので、実際の実装例として参考にしていただければ幸いです。
リポジトリ
参考
Development Containers
Create a Dev Container
Discussion
Dev Containerを活用することで、環境構築が簡単になり、チーム全体で同じ開発環境を素早く共有できるのは本当に便利ですね
コメントありがとうございます!おっしゃる通りですね。
Dev Containerのおかげで環境構築の手順書作成や個別サポートの手間が大幅減らせると思います!