📦

Devcontainerとaquaで作る再現可能な開発環境

に公開

はじめに

開発プロジェクトにおいて、メンバー間の開発環境の差異は、時として予期せぬ問題を引き起こします。「私の環境では動いているが、メンバーの環境では動かない」という状況は、皆さんも経験したことがあるのではないでしょうか。

このような問題を解決し、スムーズな開発フローを実現するためには、開発環境の統一が非常に重要です。

この記事では、Devcontaineraqua を組み合わせることで、再現性が高く、管理しやすい開発環境を構築する方法を紹介します。

Devcontainer設定の解説

Devcontainerは、Dockerコンテナーを開発環境として利用するためのVS Codeの機能です。設定ファイルをプロジェクトに含めることで、チームメンバー全員が同じツール、同じバージョンの依存関係を持つ環境を簡単に構築できます。

今回使用する設定ファイルは以下の通りです。

  • .devcontainer/devcontainer.json
  • .devcontainer/postStartCommand.sh
  • 上記以外にも、プロジェクトルートに配置する必要があるファイルとして aqua.yaml 、direnvの設定ファイルを誤ってpushしないようにするための .gitignore を作っています。

それぞれのファイルについて詳しく見ていきます。

.devcontainer/devcontainer.json の内容と解説
.devcontainer/devcontainer.json
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
// README at: https://github.com/devcontainers/templates/tree/main/src/debian
{
  "name": "Debian-and-aqua",
  // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
  "image": "mcr.microsoft.com/devcontainers/base:bullseye",

  // Features to add to the dev container. More info: https://containers.dev/features.
  "features": {
    "ghcr.io/aquaproj/devcontainer-features/aqua-installer:0.1.1": {
      "aqua_version": "v2.27.0"
    }
  },
  "remoteEnv": {
    "PATH": "/home/vscode/.local/share/aquaproj-aqua/bin:${containerEnv:PATH}"
  },
  "postStartCommand": "/bin/sh .devcontainer/postStartCommand.sh"

  // Use 'forwardPorts' to make a list of ports inside the container available locally.
  // "forwardPorts": [],

  // Configure tool-specific properties.
  // "customizations": {},

  // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
  // "remoteUser": "root"
}

主な設定項目:

  • name: Devcontainer環境の名前です。VS Codeの表示などに使われます。
  • image: ベースとなるDockerイメージを指定します。ここでは、Microsoftが提供する汎用のDebian Bullseyeベースイメージ (mcr.microsoft.com/devcontainers/base:bullseye) を使用しています。特定の言語やフレームワークに特化していないため、柔軟な環境構築が可能です。
  • features: Devcontainerに追加する機能(Features)を指定します。ここでは、aqua-installer Feature (ghcr.io/aquaproj/devcontainer-features/aqua-installer:0.1.1) を利用して、指定したバージョンのaqua (v2.27.0) をコンテナー内にインストールしています。
  • remoteEnv: コンテナー内の環境変数を設定します。PATHにaquaでインストールされたツールのバイナリパス (/home/vscode/.local/share/aquaproj-aqua/bin) を追加することで、コンテナー内でaqua管理下のツールを直接実行できるようにしています。
  • postStartCommand: コンテナー起動後に実行するコマンドを指定します。ここでは、.devcontainer/postStartCommand.shスクリプトを実行するように設定しています。このスクリプトで、aquaを使ったツールのインストールなどを行います。
.devcontainer/postStartCommand.sh の内容と解説
.devcontainer/postStartCommand.sh
#!/bin/bash
aqua i -l
direnv hook bash >> ~/.bashrc

このスクリプトは、Devcontainerが起動した後に実行され、以下の処理を行います。

  • aqua i -l: aqua.yaml(後述)に定義されたツールをすべてインストールします。-lオプションはlazy installを意味し、実際にツールが呼び出されるまでインストールを遅延させることで、コンテナー起動時間を短縮します。
  • direnv hook bash >> ~/.bashrc: direnv をシェル(ここではbash)にフックするための設定を .bashrc に追記しています。これにより、ディレクトリごとに環境変数を自動で読み込むことができるようになります。

aquaによるツール管理

aqua は、CLIツールのバージョン管理を宣言的に行うことができるツールです。aqua.yamlという設定ファイルに必要なツールとそのバージョンを記述しておくだけで、ツールが一括インストールされます。

aqua.yamlはプロジェクトルートに配置し、内容は以下のようになります。

aqua.yaml の内容と解説
aqua.yaml
---
# yaml-language-server: $schema=https://raw.githubusercontent.com/aquaproj/aqua/main/json-schema/aqua-yaml.json
# aqua - Declarative CLI Version Manager
# https://aquaproj.github.io/
# checksum:
#   enabled: true
#   require_checksum: true
#   supported_envs:
#   - all
registries:
- type: standard
  ref: v4.364.0 # renovate: depName=aquaproj/aqua-registry
packages:
- name: direnv/direnv@v2.36.0
- name: aws/aws-cli@2.27.12
- name: tfutils/tfenv@v3.0.0
- name: terraform-linters/tflint@v0.57.0
- name: Songmu/ecschedule@v0.13.0

主な設定項目:

  • registries: aquaがツールの情報を取得するためのリポジトリ(レジストリ)を指定します。aqua initを実行した際の情報から変更していません。
  • packages: インストールするツールとそのバージョンを指定します。各ツールの役割は以下の通りです。
    • direnv/direnv@v2.36.0: ディレクトリごとに環境変数を管理するためのツールです。.envrcファイルを作成し、そのディレクトリに入った際に自動で環境変数をロード・アンロードします。
    • aws/aws-cli@2.27.12: Amazon Web Services (AWS) をコマンドラインから操作するための公式CLIツールです。
    • tfutils/tfenv@v3.0.0: Terraformのバージョンを管理するためのツールです。複数のTerraformバージョンをプロジェクトごとに切り替えて使用できます。
    • terraform-linters/tflint@v0.57.0: Terraformのコードを静的解析し、ベストプラクティスに反する箇所や潜在的なエラーを検出するリンターです。
    • Songmu/ecschedule@v0.13.0: Amazon ECS Scheduled Tasks や EventBridge Scheduler の設定を宣言的に管理するためのツールです。

これらのツールをaquaで管理することにより、プロジェクトに必要なCLIツールとそのバージョンを明確に定義でき、チームメンバー全員が同じツールセットで開発を進めることができます。

環境構築のワークフロー

このDevcontainerとaquaを利用した開発環境の構築フローは非常にシンプルです。
PCの場合は前提条件を満たしていれば、、GitHub Codespacesであれば前提なしに、用意に環境を構築できます。

  1. 前提(PCの場合):
    • VS Codeがインストールされていること。
    • Docker Desktop (または互換性のあるDocker環境) がインストールされ、実行中であること。
    • VS Codeに "Dev Containers" 拡張機能がインストールされていること。
  2. プロジェクトの準備:
    • Gitリポジトリなどに、.devcontainerディレクトリとaqua.yaml、必要であれば.gitignoreを配置します。
  3. Devcontainerで開く:
    • VS CodeやCodespacesプロジェクトフォルダーを開きます。
    • .devcontainer/devcontainer.jsonを検知すると、右下に「Reopen in Container」という通知が表示されるので、それをクリックします。(または、コマンドパレット (Ctrl+Shift+P or Cmd+Shift+P) から「Dev Containers: Reopen in Container」を実行します。)
  4. 初回ビルドと起動:
    • 初回はDockerイメージのプル、Featureのインストール、postStartCommandの実行などが行われるため、少し時間がかかります。
    • コンテナーが起動し、VS Codeがコンテナーに接続されると、ターミナルでaqua i -ldirenv hookが実行され、aqua.yamlに記載されたツールが利用可能になります。
  5. 開発開始:
    • これ以降は、VS Codeを開くと自動的にDevcontainer環境でプロジェクトが開かれ、統一された開発環境ですぐに作業を開始できます。
  6. AWS CLIの設定:
    • 今回はdirenvを導入しているので、.envrcにAWS関連のアクセスキー・シークレットアクセスキーを設定する方式としています。
    • 一部のプロジェクトではMFA強制&スイッチロール方式なのですが、そこは力技で解決しています。
    MFA強制&スイッチロール方式のAWS環境の対応
    1. マネジメントコンソールでログインし、CloudShellが起動可能な権限にスイッチロールします
    2. CloudShell上からaws sts assume-roleを実行して、使いたいロールにスイッチロール、レスポンスに含まれる情報を.envrcの環境変数に設定します

副次的な効果

Devcontainer環境が整ったことで、個人的には以下のような効果もありました。

  • PC以外での緊急対応: GitHub Codespacesを利用することで、PCを持ち歩いていない場合でも軽微な修正や機能追加、保守対応が可能になりました。半日以上の外出や旅行で「PCを持っていくべきか」問題がありましたが、「iPad Proを持っていればよい」という選択肢ができたことで、心理的な負担が軽減されました。

まとめ

Devcontainerとaquaを組み合わせることで、以下のようなメリットが得られます。

  • 環境の再現性: Dockerイメージとaquaの設定ファイルにより、誰でも同じ開発環境を簡単に再現できます。
  • ツールのバージョン管理: aquaによって、プロジェクトで使用するCLIツールのバージョンを宣言的に管理し、統一できます。
  • 導入の容易さ: VS CodeのDev Containers拡張機能により、複雑な手順なしに開発環境を起動できます。
  • クリーンなローカル環境: 開発に必要なツールはコンテナー内に閉じ込められるため、ローカルマシンを汚しません。

この設定を元に、プロジェクトの特性に合わせてaqua.yamlを整備したり、拡張機能をdevcontainer.jsonで定義(customizations -> vscode -> extensions)したりと、プロジェクトに特化したカスタマイズが可能です。
ぜひ、皆さんのプロジェクトでも試してみてください。

Discussion