/devcontainer-starter

このリポジトリは、新規のプロジェクトや既存のプロジェクトにDev Containerを導入するためのスターターキットです。

Primary LanguageDockerfileOtherNOASSERTION

Dev Container Starter

このリポジトリは、新規のプロジェクトや既存のプロジェクトにDev Containerを導入するためのスターターキットです。

特徴

  • SSH経由でコンテナにログインできます。
  • エディタの起動高速化: VS Code、JetBrains IDE、Cursor、Neovimの設定をキャッシュしているので、2回目以降の起動が高速化されます。
  • Chezmoiがすぐ使えます。
  • Nixがすぐ使えます。Nixで導入したツールがVS Codeでも使えます。
  • GitHub CLI(gh)がログイン済み状態で使い始められます。
  • Direnvがすぐに使えます。
  • .bashrc.zshrcconfig.fishを「チームで共通化したい」vs「個人でカスタムしたい」が両立可能です。

必要なもの

macOS上だけで完結したい場合

リモートのLinux上のDocker環境を使いたい場合

  • リモートのLinuxでDockerが利用できる状態になっていること
  • リモートのLinuxにSSHでログインできること
  • Dev Container CLIがインストールされていること

導入方法

このリポジトリの .devcontainer ディレクトリをコピーして、自分のプロジェクトのルートディレクトリに配置します。リリースページ.devcontainerディレクトリをダウンロード&展開するワンライナーがあるので、それを使うと便利です。

ホストマシン側の準備

SSHで公開鍵認証を使う場合 (推奨)

ホストマシン側の ~/.ssh/authorized_keys に公開鍵を追加しておきます。

ghの認証情報をコンテナに引き継ぎたい場合

ホストマシンで gh auth login しておきます。

ホストマシンのChezmoiの設定をコンテナに引き継ぎたい場合

ホストマシンの ~/.local/share/chezmoi ディレクトリにChezmoiの設定ファイルを配置しておきます。

起動方法 (リモートのLinux上のDocker環境を使う場合)

ホストマシン上で以下のコマンドを実行します。

devcontainer up --workspace-folder .

起動方法 (macOS上で完結+JetBrains系IDE)

TODO

起動方法 (macOS上で完結+VS Code)

TODO

起動方法 (macOS上で完結+Cursor)

TODO

コンテナへのログイン方法

ホストマシンから直接ログイン (DevContainer CLIが使える場合)

devcontainer exec --workspace-folder . bash

この方法では、SSHエージェントが使えないため、コンテナ内でプライベートリポジトリに対してgitの操作ができません。通常の開発作業ではSSH経由でログインすることをお勧めします。

ホストマシンから直接ログイン (DevContainer CLIが使えない場合)

docker ps でコンテナのIDを調べて、以下のコマンドを実行します。

docker exec -it <container-id> bash

この方法では、SSHエージェントが使えないため、コンテナ内でプライベートリポジトリに対してgitの操作ができません。通常の開発作業ではSSH経由でログインすることをお勧めします。

SSH経由でログイン (推奨)

ssh -p 2222 vscode@127.0.0.1

基本的な設定は以下の通りです。

Host devcontainer
  HostName 127.0.0.1
  Port 2222
  User vscode
  StrictHostKeyChecking no
  UserKnownHostsFile /dev/null

StrictHostKeyChecking noUserKnownHostsFile /dev/null は、セキュリティ的には望ましくない設定ですが、開発環境として使うためには便利です。 Dev Containerは日々破棄と構築が繰り返されることが前提であるため、フィンガープリントがちょくちょく変わることがあります。 これらをセットしておかないと、~/.ssh/known_hosts を日々メンテナンスする必要が出てきます。 したがって以上の設定をしておき、フィンガープリントの確認をスキップする設定にしておいたほうが、開発体験が向上します。

これに加えて、SSHエージェントの転送を有効化しておくと、コンテナ内でプライベートリポジトリに対してgitの操作ができます。

TODO: SSHエージェントの転送の設定方法を追記する

鍵を使わない場合、上の「直接ログイン」の方法でログインし、sudo passwd vscodeでパスワードを設定しておく必要があります。開発環境として使うため、セキュリティ的には望ましくないと思われるので、鍵を使うことをお勧めします。

コンテナへのログインは以下の通りです。

ssh devcontainer

Dev Containerイメージの再ビルド

devcontainer build --workspace-folder .

起動中のDev Containerを削除しつつ、再ビルド、再起動まで一度に行う場合は次のコマンドを実行します。

devcontainer up --workspace-folder . --remove-existing-container

Dev Containerの削除

devcontainer downコマンドは未実装のため、代わりに次の手順を行います。

docker compose lsで当該プロジェクト名を調べます。

見つかったプロジェクト名を使って以下のコマンドを実行します。

docker compose --project-name <project-name> down

ただし、この方法では削除されるのはコンテナとネットワークだけです。イメージやボリュームもまとめて削除したい場合は以下のコマンドを実行します。

docker compose --project-name <project-name> down --volumes --rmi all --remove-orphans

このコマンドの詳細は《滅びの呪文》Docker Composeで作ったコンテナ、イメージ、ボリューム、ネットワークを一括完全消去する便利コマンド #docker-compose - Qiitaを参照してください。

チームの設定と個人向けの設定

このDev Containerは、チーム開発向けの設定と個人向けの設定を両立させることを目指しています。

Bash, Zsh, Fishの設定

チーム共通の設定は、

  • ~/team.bash
  • ~/team.zsh
  • ~/team.fish

に記述します。これは、それぞれ

  • ~/.bashrc
  • ~/.zshrc
  • ~/.config/fish/config.fish

から読み込まれることを想定しています。したがって、シェルを個人的にカスタマイズしたい場合は、これらのファイルを編集して構いませんが、チーム共通の設定を忘れずにsourceすることを推奨します。

Chezmoiの適用

dotfilesをChezmoiで管理している場合は、すでにchezmoiコマンドが使える状態になっているので、Dev Container内で以下のコマンドを実行します。

chezmoi init --apply

Nixの適用

Dev Containerにはnixコマンドがインストールされているので、Nixの設定ファイルを持っている場合は、Dev Container内で以下のコマンドを実行します。

nix develop # など

このDev Containerには、Direnvが導入済みなので、.envrcファイルを使って環境変数を設定することもできます。おそらくこちらの設定のほうが、毎回nix developを実行する必要がないので便利でしょう。

# /workspace/.envrc
use flake

VS CodeでのNixパッケージの利用

プロジェクトルートのflake.nixで指定したパッケージをVS Codeで利用するためには、direnv拡張をVS Codeにインストールしてください。

プロジェクトルートに.envrcを作り、次の内容を書いておいてください。

use flake

この状態でVS Codeを開くと、direnv allowを求められます。許可するとNixのパッケージにパスが通るので、VS Codeの各種拡張からNixで導入したツールが利用できるようになります。許可してもうまくパスが通っていないようであれば、VS Codeを再起動してみてください。

複数のプロジェクトを同時に起動したいとき

複数のプロジェクトを同時に起動しようとすると、ポートの競合が発生します。

.devcontainer/.envHOST_ADDRをプロジェクトごとにIPアドレスを変えることで、一応ポートの競合を回避できます。

ただし、この方法は開発者個々人がIPアドレスを自由に決める裁量がないため、チーム開発には向いていない可能性があります。開発者ごとに携わっているプロジェクト数が異なる可能性があるためです。このあたりの改善は今後の課題とします。

CI (GitHub Actions)

devcontainer/ciでDev Containerを再利用する

GitHub ActionsでDev Containerを再利用したいときは、devcontainer/ciアクションを使うと良いでしょう。

name: Dev Container Build Check
on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  build:
  runs-on: ubuntu-latest
  steps:
    - uses: actions/checkout@v4
    - name: Run devcontainer build
      uses: devcontainers/ci@v0.3
      with:
        runCmd: yarn test

以上はシンプルな導入例ですが、ドキュメントにはDev Containerのビルドをキャッシュする方法など高度なトピックについても記載されています。

devcontainer/ciを使わずにDev Containerのビルドをテストする

Dev ContainerがビルドできるかをCIでテストすることができます。以下のようなGitHub Actionsの設定ファイルを用意して、リポジトリに配置します。

name: Dev Container Build Check

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  devcontainer-build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Install Dev Container CLI
        run: npm install -g @devcontainers/cli
      - name: Run devcontainer build
        run: devcontainer build --workspace-folder .

devcontainer/ciを使わずにDev Containerで何かコマンドを実行する

CIでもDev Containerの環境を用いて、テストやビルドなど何らかのタスクを行いたいことがあるかもしれません。 その場合は、以下のようなGitHub Actionsの設定ファイルを用意して、リポジトリに配置します。

name: Use Dev Container in CI

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  devcontainer-build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Install Dev Container CLI
        run: npm install -g @devcontainers/cli
      - name: Execute something in Dev Container
        run: devcontainer exec --workspace-folder . echo "Hello, Dev Container!"

Dev Containerのカスタマイズ

このDev Containerをカスタマイズする方法はいくつかあります。

devcontainer.json

Dev Containerの設定ファイルです。本リポジトリでは扱っていない設定も沢山あります。カスタマイズが必要になったら下記公式ドキュメントを参照してみてください。

Dev Container Features

Dev Container Featuresは、開発コンテナの機能を拡張するためのモジュラーなコンポーネントです。これらを使用することで、開発者は必要な言語サポート、ツール、設定などを柔軟に追加し、プロジェクト固有の要件に合わせて開発環境をカスタマイズできます。再利用可能で、バージョン管理が可能なため、一貫性のある環境構築を容易にします。

このDev ContainerでもすでにいくつかのFeaturesを利用しています。

利用可能なDev Container Featuresの一覧は以下のURLから確認できます。

https://containers.dev/features

起動パフォーマンス

Dev Containerの起動が遅いと、開発体験が悪くなります。そのため、Dev Containerの起動を高速化は重要です。

Dev ContainerはDockerをベースとしているため、起動を高速化するにはDockerビルドの最適化に関する次のような知識が必要です。

  • イメージを小さくする
  • レイヤーキャッシュがよく効くようにする (単純にレイヤー数を減らせば速くなるわけではなく、適切に分割することが重要)
  • コンテキストをできるだけ小さくする
  • ボリュームを使う

イメージサイズを小さくする・レイヤーキャッシュを効かせる

この手の最適化はdivedocker diffなどのツールを使って、Dockerイメージのレイヤーを分析する必要があります。

コンテキストをできるだけ小さくする

Dockerビルド時にDockerデーモンに転送するファイルが増えると、ビルドが遅くなります。 Dev Containerを構成する上で不要なファイルがコンテキストに含まれていないかチェックしましょう。 チェックにはdocker-show-contextを使うと便利です。不要なファイルが除外されるように、コンテキストを指定する、.dockerignoreファイルを適切に設定するなどの対策を行いましょう。

ボリュームを使う

Dev Containerの開発体験はDockerビルドのパフォーマンスだけでなく、IDEの起動にかかる時間や、yarn installなどの初回セットアップ手順にかかる時間も重要になってきます。 このDev Containerでは、「ボリュームを使う」アプローチを用いて、初回セットアップ手順の高速化を図っています。 この最適化はツールがどこにキャッシュや設定ファイルを作るかを理解する必要があります。 ツールがどこにファイルを作るかを知るには、docker diffコマンドを使うと便利です。

docker diff <container-id> | sort

これにより見つかったファイルをボリュームにマウントすることで、2回目以降のセットアップ手順の高速化が期待できます。 ボリュームマウントする方法は、devcontainer.jsonmountsセクションを使うと簡単に設定できますが、このDev Containerでは docker-compose.yml を使ってボリュームマウントを設定しています。

Q&A

vsc-{project-name}-{checksum}-uid:latestのようなイメージが作成されるのはなぜですか?

Dev Containerの仕様で、設定updateRemoteUserUIDが有効になっているとき、ホストマシンのユーザーIDに合わせてコンテナ内のユーザーIDを変更します。この変更がイメージとして作られた結果です。

ベースにした設定

このDev Containerは以下のリポジトリを参考に構成されています。