GameWith Developer Blog

GameWith のエンジニア、デザイナーが技術について日々発信していきます。

GameWithでDev Containerを導入した時のポイント #GameWith #TechWith

はじめに

こんにちは。サービス開発部の木村です。

アドベントカレンダー2日目の記事として、最近GameWithでDev Containerを導入したので、その経緯や導入時のポイントを紹介します!

Dev Containerとは

Dev Containerとは、開発するアプリケーションだけでなくエディタの拡張機能などの開発環境もまとめてコンテナ化することで、より共通化された開発環境を簡単に再現するための仕組みです。

Development containers

Dev ContainerはVSCodeやIntelliJ IDEAなどの複数のツールでサポートされていたり、Dev Containerを導入したら開発者全員が利用を強制されるわけではなく恩恵を感じている使いたい人だけが使えるので気軽に導入しやすいです。

Dev Container自体の詳しい説明や導入方法は、公式ドキュメントや多くの記事で紹介されているので今回は触れませんが、各種エディタが提供しているドキュメントを参照するのがわかりやすいのでおすすめです。

GameWithではVSCodeベースのエディタの利用者が多いため、本記事ではVSCodeベースでの内容になります。

Dev Containerを導入した経緯

今回はバックエンドでFuelPHP、フロントエンドのビルド環境でNode.jsが使用されているモノリシックなWebアプリケーションのリポジトリにDev Containerを導入しました。

元々Dockerでアプリケーション自体はコンテナ化はされていましたが、VSCode拡張機能はローカルマシンにインストールされているPHPやNode.jsを参照してしまうため、結局各言語のバージョン等は開発者ごとに管理が必要な状態でした。

Dev Containerを導入してVSCodeの拡張機能等もコンテナ化して開発環境の管理をもっと楽にしたいという話題がチーム内で出たため、今回試しに導入してみることにしました。

Dev Container導入時のポイント

GameWithでDev Containerの導入を進める際に、実用性を考慮して調整した箇所などをいくつかご紹介します。

複数環境のコンテナ化

導入したリポジトリではローカル開発において2つの環境があり、Dev Containerでもそれらを使い分けれるようにしました。

  • remote: 開発環境のDBに接続して開発をするための環境
  • local: ローカルマシン内でDBを立ち上げて、外部とは接続せずに開発するための環境

Dev Containerで複数環境を使い分けるには、次のように/.devcontainer内をディレクトリで区切り、それぞれにdevcontainer.jsonを設置することで、コンテナ起動時に開発者が環境を選んでコンテナを起動できます。

/.devcontainer/
   ├─ local/
   │     └─ devcontainer.json
   └─ remote/
         └─ devcontainer.json

devcontainer.jsonnameがコマンドパレット上で選択肢として表示されます。

Named volumeでコンテナのパフォーマンスを改善

Dev Containerを試している際に、コンテナ起動後にgitがつかえるようになるまで長く時間がかかってしまう問題がありました。git statusに20秒以上、git pushにも10秒以上かかってしまい、開発が円滑にできるとは言えない状態でした。git以外のツールにも若干影響はありましたが、gitへの影響が顕著に出ていました。

この問題の原因は、コンテナ内のファイルが多すぎるせいでgitのファイル追跡に時間がかかっているようでした。そのため、次のようにnode_modulesや各種ツールのキャッシュ系ファイルなどのgitで追跡しない大量なファイルが生成されるディレクトリを、次のようにdevcontainer.jsonmountsで別のvolumeとしてマウントさせることで改善できました。

{
    ...
    "mounts": [
        "source=${localWorkspaceFolderBasename}-node_modules,target=${containerWorkspaceFolder}/node_modules,type=volume",
        ...
    ]
}

開発作業特化のコンテナ調整

既存のDockerコンテナはあくまでもアプリケーションを動かすため環境で、そのまま開発で使うと不便な点もありました。次は一例です。

  • コンテナにvimなどのエディタがインストールされていなく、デフォルトでgit rebase等ができない
  • コンテナのターミナルで日本語の表示・入力ができない

そのためDev Container専用のコンテナの設定を用意し、アプリケーション用のコンテナを上書きしています。

既存のアプリケーション用のdocker-compose.ymlは大まかに次のようになっており、DB関連のコンテナも一緒に立ち上げています。(各コンテナの詳細な設定は省略しています。)

# /docker/remote/docker-compose.yml
version: "3.7"

services:
  nginx:
    image: nginx
    restart: always

  app:
    build:
      context: .
      dockerfile: infrastructure/docker/Dockerfile-app
    volumes:
      - .:/var/www/gamewith
    working_dir: /var/www/gamewith

  db:
    build:
      context: .
      dockerfile: infrastructure/docker/Dockerfile-mysql

  memcached:
    build:
      context: .
      dockerfile: infrastructure/docker/Dockerfile-memcached

  redis:
    build:
      context: .
      dockerfile: infrastructure/docker/Dockerfile-redis

今回調整したかったのはDev Containerで実際にアクセスするappコンテナのみなので、次のように/.devcontainer/docker-compose.ymlを作成し、appコンテナだけDev Container用のDockerfileを参照するように設定しています。こうすることで上書きされていないdbredisコンテナは引き続き既存のDockerfileを参照してくれます。

# /.devcontainer/docker-compose.yml
version: "3.7"

services:
  app:
    image: app
    build:
      context: .
      dockerfile: .devcontainer/Dockerfile-app
    volumes:
      - .:/var/www/gamewith
    working_dir: /var/www/gamewith

Dev Container用に作成した/.devcontainer/Dockerfile-appでは次のように、既存のアプリケーションの設定に加えて、vimやlocaleのセットアップをしています。

# /.devcontainer/Dockerfile-app
FROM php:8.1-apache-bullseye

# アプリケーションに必要な設定やツールのインストール
...省略

# PHPアプリケーションのランライムでは使用しないけど、コンテナのターミナルで実行したい人向けのNode.jsなどをインストール
...省略

# git rebaseなどできるようにvimをインストール
RUN apt-get install -y vim

# DevContainerのターミナルで日本語が使用できるように、コンテナのlocaleを日本語に設定する
RUN apt-get install -y locales \
    && sed -i '/ja_JP.UTF-8/s/^# //g' /etc/locale.gen \
    && locale-gen \
    && update-locale LANG=ja_JP.UTF-8

ENV LANG=ja_JP.UTF-8
ENV LANGUAGE=ja_JP:ja
ENV LC_ALL=ja_JP.UTF-8

そして/.devcontainer/docker-compose.ymlを適用するには、devcontainer.jsondockerComposeFiledocker-compose.ymlのパスを優先度が低い順に定義します。(一部コンテナのみを上書きするのではなく、全てDev Container用のコンテナを使用する場合は、既存のdocker-compose.ymlを定義する必要はありません。)

{
    "name": "Remote Container",
    "dockerComposeFile": [
        "../../dokcer/remote/docker-compose.yml", // 既存のアプリケーションのdocker-compose.yml
        "../docker-compose.yml" // Dev Container用のdocker-compose.yml
    ]
}

こうすることで、Dev Containerで起動した時のみappコンテナに開発作業用packageのインストールや設定を行うことができます。

ただし、あまりにも実際にアプリケーションを動作させる環境と乖離しすぎてしまうとコンテナ開発をしている意味が無いので、用法・用量は守りましょう。

おわりに

Dev Containerを導入してみて下記が改善されました。

  • ローカル開発において、今まで以上におま環が発生しにくくなった。
  • PC交換時などのセットアップ時も必要な手順が減って楽になった。
  • ローカルマシンのグローバルを参照してしまうことで問題が発生してしまうVSCode拡張機能の挙動を避けれるようになった。

そして、逆に運用少し難しいと思っていることもあります。

  • 個人で利用したい拡張機能の管理に若干手間がかかる。
    • 手間なのは主に初回セットアップ時や新しく拡張機能を使用する時だけなので、そこまで大きな問題ではありません。
    • こちらに関してはまた個別で記事にする予定です!
  • パフォーマンスの改善はしているものの、まだローカルに比べてツール系の動作が遅いケースがあるので調整が必要。

総合的に開発体験は良くなったので、調整を続けて他のリポジトリでも積極的に導入しようと思っています。

Dev Containerの導入を検討している方は改めてぜひお試しください!


こんなGameWithではエンジニアを絶賛募集中です!

サーバーエンジニアやフロントエンジニアの方、AIに興味がある方や、Unityでの開発に興味がある方もお気軽にカジュアル面談をお申し込みください!

github.com