DevContainerは2018年にMicrosoftがVSCodeで拡張機能のRemote - Container
として「Dockerコンテナ内でコードを編集・デバッグ」機能として実装しました。
すでに、歴史的な話の一環で出てきましたが、DevContainerは「Dockerコンテナ内でコードを編集・デバッグ」するための機能です。
この機能を使うことで「ホストOS上の環境を変更」することなく、使用するSDKのバージョンを切り替え(コンテナの切り替え)ながら開発ができるようになります
また、VSCodeの拡張機能の「Dev Containers」を使って接続することで、コンテナごとにVSCode内で使用する拡張機能を指定することができるので、無駄なVSCode拡張がないクリーンな状態で開発ができます。
Dockerは導入済みの前提の話になります。
もしまだ導入していない方は、公式サイトからダウンロードインストールしておいてください。
VSCode拡張機能マーケットプレイスから、「Remote Devolopment」をインストールします
この拡張には、「Dev Containers」「Remote – XXXX」系の拡張やWSL(Windows用)の拡張などいろいろはいっているのでとりあえずこれを入れておけばDockerを使った開発には困らないと思います。
まずは、シンプルな既存のイメージをそのまま使用する方法をご紹介します。
イメージを取得するには、「取得するイメージのリポジトリ」から使いたいイメージを探す必要があります。
イメージの選定と指定方法
これ以外にも「プライベートリポジトリ」なども指定可能です。
各リポジトリの「URLとイメージのバージョン」をimage
プロパティに対して""
で囲い以下のように記述します。
Docker Hub上にあるイメージを使用する場合は、URLなどは必要なく、メジャーなもイメージであれば、「イメージ名:タグ」で大体取得できます。
# Docker Hub
image: "php:latest"
# GitHub Container Registry
image: "ghcr.io/OWNER/REPOSITORY[:TAG]"
# Azure Container Registry
image: "<registry-name>.azurecr.io/<repository>[:tag]"
フォルダ構成とdevcontainer.jsonへの最低限の記述
まずはファイル構成として以下のような構成にしました。
dev-container-php-test/
├── .devcontainer/
│ └── devcontainer.json
└── main.php
PHPの解説はしませんが、DevContainer内でmain.phpのコードを動かすことができれば作業は成功です。
DevContainerを使うために必要なフォルダ構造としては、.devcontainer
フォルダとその中にあるdevcontainer.json
になります。
devcontainer.jsonには最低限の設定として以下のようにname
プロパティとimage
プロパティを指定します。
{
"name": "Dev PHP",
"image": "php:latest"
}
- name : DevContainerの名前をつける。複数使う場合に見分けがつきやすいようにする程度の意味
- image : DevContainerで使用するイメージを指定する。ローカルのDockerfileからビルド済みのイメージであれば使用可能
nameプロパティは指定しなくてもエラーにはならずDevContainerを動かすことができます。
つけたときとつけなかった時の違いとしては、以下2つの画像の赤枠の中の部分に違いが出ます。
実行
main.php
は空のままでいいので、DevContainerでコンテナに接続したあとに編集して実行します。
DevContainerを実行するには、いくつか方法がありますが、今回はマウスクリックで接続する方法を試します。
今回のプロジェクトのルートディレクトリであるdev-container-php-test
フォルダをVSCodeで開いてください。
dev-container-php-test/ <- ここのフォルダをVSCodeで開く
├── .devcontainer/
│ └── devcontainer.json
└── main.php
その後、VSCodeの左下の青いボタンを押します。
そうすると、画面中央上にポップアップが表示されるので、その中のコンテナで再度開く
を選択することでDevCotntainerでコンテナに接続する事ができます。
事前に手動でコンテナなどを実行しておく必要はありません。
VSCodeがいい感じに準備してくれるのでDocer Desktopだけ起動させておけばOKです。
VSCodeが一度落ちて再度起動して、一見変わっていないような状態で動き出せば接続成功です。
ここで、main.phpに以下の内容を記述してください
<?php
echo "テスト\n";
その後、「ターミナルを開く」と以下のようなLinuxでよく見るターミナルが表示されます。
以下のコマンドを実行することで、同じ出力がされれば成功です。
# コマンド
root@6b8fa1d565b9:/workspaces/PHP# php main.php
# 出力
テスト
DevContainerを終了させる
DevContainerを終了させるのも接続時と同じようにできます。
VSCodeの左下の青いボタンを押します。
画面中央上にポップアップが表示されるので、その中のリモート接続を終了
するを選択することでDevCotntainerでコンテナに接続する事ができます。
切断をしても編集していたファイルはホストOSからのマウントなので編集状況を維持することができます。
今回は、Go言語の開発環境を構築していこうと思います。
今回はDevContainerがマウントするフォルダを設定して任意のフォルダにマウントできるような設定を追加、さらに、VSCodeの拡張機能を入れて開発がよりしやすい環境を構築します。
DevContainerで開発するための開発用フォルダを作成します。
ファイルの中身は空で大丈夫なので、フォルダとファイルを追加して以下ディレクトリ構造と同じフォルダを作成してください。
dev-container-test/
├── .devcontainer/
│ └── devcontainer.json
├── Dockerfile
└── main.go
.devcontainer(フォルダ)
: devcontainer.jsonをここに配置すると決まっているdevcontainer.json
: DevContainerでコンテナに接続する時の設定が書いてあるDockerfile
: オリジナルイメージを作成するために使用するmain.go
: Goのソースコード
作成したディレクトリを今回は先にVSCodeのフォルダーを開く
で開いてこれ以下のファイルの編集をしていきます。
まずは、devcontainer.json
に対して以下の内容を記述します。
{
"name": "Go DevContainer",
"build": {
"dockerfile": "../dockerfile" // 相対パス
},
"workspaceFolder": "/workspace",
"workspaceMount": "source=${localWorkspaceFolder},target=/workspace,type=bind,consistency=cached",
"customizations": {
"vscode": {
"extensions": [
"golang.go"
]
}
}
}
以下、記述してある内容の説明になります。
- name : DevContainerの名前を指定します。この名前はVS Codeのステータスバーやその他のUI要素で表示され、複数のDevContainerを扱う際の識別に役立ちます。
- build : コンテナのビルドに関する設定を指定します。主に使用するDockerfileのパスを定義します。
- dockerfile: 使用するDockerfileへのパスを指定します。この例では、devcontainer.jsonが配置されている.devcontainer/ディレクトリから見た相対パス../dockerfileを指しています。
- workspaceFolder : コンテナ内でのワークスペースディレクトリを指定します。ホストマシンのプロジェクトディレクトリがこの場所にマウントされます。
- 注意点: デフォルトでは、
/workspaces/<project-folder>/
が使用されますが、カスタマイズする場合は以下のworkspaceMount設定も合わせて調整してください。
- 注意点: デフォルトでは、
- workspaceMount : ホストマシンのプロジェクトディレクトリをコンテナ内のworkspaceFolderにバインドマウントする設定です。
- ${localWorkspaceFolder} : ホストOSのルートフォルダのパスを持っている変数
"source=バインドしたいホストOSの絶対パス,target=バインド先のコンテナ内のパス,type=bind,consistency=cached"
- customizations : コンテナ内のVS Codeのカスタマイズ設定を指定します。
- vscode : VS Codeの設定を行います。ここでは、必要な拡張機能を指定しています。
- extensions : コンテナ起動時に自動的にインストールされるVS Codeの拡張機能をリストで指定します。
- vscode : VS Codeの設定を行います。ここでは、必要な拡張機能を指定しています。
Dockerfileに記述
Dockerfileには「使用するイメージ」ぐらいしか記述しませんが、今後イメージにツールをいれる可能性を考慮してDockerfileを使用しています。
FROM golang:1.23.2
main.goに記述
最後にmain.go
に対して以下のコードを記述してサーバーとして、ポート番号8080で動くようなコードを作成します。
http://localhost:8080
にアクセスすると「やあ」と表示されるはずです。
package main
import (
"fmt"
"log"
"net/http"
)
func main() {
http.HandleFunc("/", Handler)
err := http.ListenAndServe(":8080", nil)
if err != nil {
log.Fatal()
}
}
func Handler(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "やあ")
}
実行
これで必要なフォルダ構造の準備はできたので、実際にDevContainerでコンテナに接続しながらmain.goを動作させてみようと思います。
今まで編集していたフォルダを開いている状態のVSCodeで「コマンドパレット」を開いて「Dev Open Folder in」ぐらいまで打ち込んで、「開発コンテナー: コンテナーでフォルダーを開く」を実行します。
その後、「Finder or エクスプローラー」が開くので、そのまま「開く」を押すと、今のフォルダでDevContainerを実行する事ができます。
以下の画像のような状態でVSCodeのエクスプローラー部分が「WORKSPACE[開発コンテナー...]
」となっていれば接続成功です。
ターミナルを開くと、Linuxでよく見るBashが実行されると思います。
すでにカレントディレクトリでコマンドを実行するだけの状態になっていると思うので、以下のコマンドを実行します。
root@6b26cff87908:/workspace# go run main.go
実行後、ブラウザでhttp://localhost:8080
を開くことで「やあ」が表示されれば正常に動いています。
ポートの開放は手動しなくてもいい
DevContainerでは、通常のコンテナと違い、ポートをホストOSのポートに手動でバインドしていなくても、コンテナ内で実行されるアプリケーションが特定のポートを使用する必要がある場合、自動的にバインドされ、ホストOS側でアクセスできる様になっています。
切断
VSCodeの左下の青いボタンを押します。
画面中央上にポップアップが表示されるので、その中のリモート接続を終了
するを選択することでDevCotntainerの接続を解除することができます。
切断をしても編集していたファイルはホストOSからのマウントなので編集状況を維持することができます。
DockerfileとDevContaienrを使う場合のWorkspaceの整合性をとる
dockerfileの最終的なWORKDIRは、/workspace
にするようにすることで、DevContainerとの整合性が取りやすくなっています。
devcontainer.json
側も同様にすることで、dockerfile
の最終的なディレクトリと、DevContainer側の作業ディレクトリを合わせることができるので整合性が取りやすいのでこのようにしています。
# === 省略 ===
"workspaceFolder": "/workspace"
# === 省略 ===
ケースとしてはかなりレアかもしれませんが、実験としてはわかりやすい結果が出たのでまとめています。
Dockerfile
と、devcontainer.json
で以下のような定義をしていた場合
FROM golang:1.23.2
# golangのデフォルトのパスが/goなのでバインドマウントしたディレクトリに移動
WORKDIR /workspace
# test.txtを/workspaceに作成する
RUN touch test.txt
{
"name": "Go DevContainer",
"build": {
"dockerfile": "../dockerfile" // 相対パス
},
"workspaceFolder": "/workspace",
"workspaceMount": "source=${localWorkspaceFolder},target=/workspace,type=bind,consistency=cached",
"customizations": {
"vscode": {
"extensions": [
"golang.go"
]
}
}
}
Dockerfile
の最後の処理であるtouch test.txt
を実行すると当然、/workspace
内にtest.txt
が生成されるが、このあとに、DevContainerの接続がされて、バインドマウントが実行され、/workspace
内にあるファイルはすべて上書きされて消えてしまいます。
もしどうしてもファイルを作成したいなら以下の2パターンの手法があります。
"postCreateCommand": "touch /workspace/test.txt"
をdevcontainer.jsonに追記して、コンテナ接続後に生成する- 手動でホストOSのディレクトリ内に作成しておく
サーバーを起動すると、今までの設定では、ホストOS上のlocalhost:ポート番号
にバインドされていましたが、これでは、ホストOS以外のデバイスからはアクセスができない状態になります。
それは、Portforardが必ずLocalhostとしてポートをバインドするからです。
これを外部に公開するには、runArgs
の-p
オプションで明示的にバインドする必要があります。
以下のdevcontainer.json
が外部からのアクセスに対応したものになります。
{
"name": "Go DevContainer",
"build": {
"dockerfile": "../dockerfile"
},
"workspaceFolder": "/workspace",
"workspaceMount": "source=${localWorkspaceFolder},target=/workspace,type=bind,consistency=cached",
"customizations": {
"vscode": {
"extensions": ["golang.go"]
}
},
// ここを追加して、forwardPortsを削除
"runArgs": [
"-p",
"8080:8080" // ホストの全てのインターフェースにバインド
]
}
外部からのアクセスには、ホストOSのIPが必要になるので、ホストOS上で以下のコマンドを実行してIPを調べてください。
# コマンド
ifconfig | grep 192
# 出力
inet 192.168.0.33 netmask ....
調べたIPを使って外部からアクセスできるようになると思います。(サーバーを動かすのを忘れずに)
http://ホストOSのIPアドレス:8080
私の環境では以下のようになります。
http://192.168.0.33:8080
DevContainerを使いこなすことで、ホストOS上に開発環境を構築せずにすむ上に、デバイスの移行時などに開発用のディレクトリを共有してしまえばすぐに移行ができるので、とても便利な機能だと思います。