GitHub の Codespaces を開発環境として導入する
GitHub の Codespaces を開発環境として導入する

GitHub の Codespaces を開発環境として導入する

はじめに

この記事では、弊社の開発環境を GitHub の Codespaces に構築した際の構成などをシェアします。

Codespaces を構築したモチベーションは、開発環境に関する以下のような課題に直面していたからです。

  1. ⏳ 開発環境のセットアップに時間が掛かる
  2. 🤓 新しく参加される方がセットアップにハマった際に、既存メンバーによるサポートの負荷が割と高い
  3. 📑 セットアップのドキュメントを整備していくコストが高い (もし xxx なら yyy をしてください、など注釈が必要になってきて、書く方も読む方も複雑なものと向き合う必要がある)
  4. 💻 Linter や Formatter が IDE の Extension に依存していて、設定を合わせるのが微妙に面倒
  5. 👩‍💻 リモートであるがゆえに席に来てもらって開発中の画面を触ってもらう、などが出来ない (例えばアニメーションなど)

GitHub Codespaces を使う事で、このような悩みが解決可能だと考え、導入を検討し実際に開発環境としていれてみました。

GitHub Codespaces とは?

このブログを読むとだいたいわかります。

もちろん我々とは規模が全く違いますが、課題感などはとてもシンパシーが湧く内容で、かつそれをいかに解決していったのかというストーリーが単純に読み物として面白いです。またそうやって作られたものが GitHub の機能として提供されているかと思うと感慨深いものもあります。

Codespaces にご興味をお持ちの方は是非ご一読をおすすめします。

動作のイメージ

Codespaces は devcontainer という開発用のコンテナを GitHub 管理下に建てて、そこに VSCode やブラウザを接続して使います。

試してはいませんが GitHub の提供する cli 経由で ssh も可能との事です。

image

DROBE の開発のセットアップの流れ

devcontainer の作り方

それでは、devcontainer の作りかたを解説していきます。

repository の root に ./devcontainer を作り devcontainer.json を配置します。

devcontainer.json は Codespaces の設定ファイルで、devcontainer の実態となる Dockerfile を指定する事が多いです。

devcontainer.json で指定された Dockerfile は devcontainer の build に使われます。

.devcontainer/
├── Dockerfile
└── devcontainer.json
.devcontainer フォルダの内部

devcontainer.json は以下のようなものになります

{
  // devcontainer を作るのにつかう Dockerfile
  "build": {
    "dockerfile": "Dockerfile"
  },

  // 後で説明する Docker-in-Docker の設定
  // ref. https://github.com/microsoft/vscode-dev-containers/blob/main/containers/docker-in-docker/README.md
  "runArgs": ["--init", "--privileged"],
  "mounts": ["source=dind-var-lib-docker,target=/var/lib/docker,type=volume"],
  "overrideCommand": false,

  // Visual studio code の Remote [Codespaces]-scoped settings の設定 codespace
  "settings": {
  },

  // Add the IDs of extensions you want installed when the container is created.
  "extensions": [
  ],

  // prebuild
  "onCreateCommand": "cd /workspaces/drobe && sh script/codespace/prebuilt.sh", // 後で説明する prebuild される時に実行されるコマンド

  // post build
  "postCreateCommand": "service redis-server start" // devcontainer が作られた後に実行されるコマンド
}
devcontainer.json

Dockerfile は devcontainer を作るたびに作られるので、先に必要なセットアップが終わったものを準備しておき、ここではそのコンテナを pull するだけにしています

FROM ghcr.io/{org-name}/{repo-name}/{dev-container-name}:{hash}
Dockerfile
DROBE における devcontainer の概要図
DROBE における devcontainer の概要図

Codespaces の起動

./devcontainer をセットアップした上で repository の Code ボタンから Codespaces を起動できるようになります。

  1. 緑色の「 <> Code ∨」ボタンを押し、モーダル内の「Codespaces」タブを選択し、ボタンのプルダウンから「Configure and create codespace」を選択してボタンをクリックします。
  2. CodeSpaces タブ
    CodeSpaces タブ
  3. ブランチとマシンタイプを選択します
  4. ブランチとマシンタイプの選択
    ブランチとマシンタイプの選択
  5. setting up your codespace という画面が表示されるので、そのまま待ちます (ここで vscode desktop をクリックしてローカルの vscode を起動しても大丈夫です)
  6. 起動待ち画面
    起動待ち画面
  7. セットアップが完了すると、自動的に新規作成された codespace に接続した Visual Studio Code for web が開きます
  8. ブラウザ上で起動した VSCode
    ブラウザ上で起動した VSCode
  9. Terminal が使えるようになるので、セットアップに必要なコマンドなどを叩けます
  10. image
  11. docker-compose などでアプリケーション群を立ち上げると、自動的にポートが開放されます TERMINALの横のPORTSタブを開くと、各アプリケーション用に開かれたポートと、そこへアクセスするためのURLが表示されます Local AddressのURLにアクセスすると実際にcodespaceで稼働しているアプリケーションを見ることが出来ます
  12. image

docker in docker

DROBE は開発に docker-compose を使っているため、Codespaces 環境では docker-in-docker を使う事になります。

弊社の場合の Dockerfile は MS の docker-in-docker 用の script を使って以下のように作っています

FROM php:8.0.7-cli

~ 中略 ~

# [Option] Install zsh
ARG INSTALL_ZSH="false"
# [Option] Upgrade OS packages to their latest versions
ARG UPGRADE_PACKAGES="false"
# [Option] Enable non-root Docker access in container
ARG ENABLE_NONROOT_DOCKER="false"
# [Option] Use the OSS Moby Engine instead of the licensed Docker Engine
ARG USE_MOBY="false"

# Install needed packages and setup non-root user. Use a separate RUN statement to add your
# own dependencies. A user of "automatic" attempts to reuse an user ID if one already exists.
ARG USERNAME=none
ARG USER_UID=0
ARG USER_GID=$USER_UID
# Copy library scripts to execute
COPY library-scripts/*.sh library-scripts/*.env /tmp/library-scripts/

RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
    # Remove imagemagick due to https://security-tracker.debian.org/tracker/CVE-2019-10131
    && apt-get purge -y imagemagick imagemagick-6-common \
    # Install common packages, non-root user
    && bash /tmp/library-scripts/common-debian.sh "${INSTALL_ZSH}" "${USERNAME}" "${USER_UID}" "${USER_GID}" "${UPGRADE_PACKAGES}" "true" "true" \
    && apt-get autoremove -y && apt-get clean -y && rm -rf /var/lib/apt/lists/*

# Use Docker script from script library to set things up
RUN bash /tmp/library-scripts/docker-in-docker-debian.sh "${ENABLE_NONROOT_DOCKER}" "${USERNAME}" "${USE_MOBY}" \
    && apt-get autoremove -y && apt-get clean -y && rm -rf /var/lib/apt/lists/*

~ 略 ~

Dockerfile 内部で叩かれている common-debian.shdocker-in-docker-debian.sh は MS 公式の devcontainer の定義 repo から拝借しています

また、上記は設定にあたってコチラのブログを大変参考にさせていただきました (この場を借りてお礼させて頂ければと思います)

prebuild

ここまでで Codespace の利用の設定は完了で、実際に開発を始める事が可能です。 ただしこの状態だと、Codespace でインスタンスを作る度に (Dockerfile に書いていない) セットアップのコマンドなどは自分で叩く必要があります。 それでも開発環境としては十分に使い勝手は良いと言えると思いますが、もっと起動を高速化したくなるのが人情というものです。

そこで利用できるのが prebuild という仕組みです。 prebuild を利用すると、devcontainer に対してさらにセットアップのコマンドなどを実行したものをベースの環境として開発を始められるようになります。

prebuild の概要
prebuild の概要

設定は簡単で、まずは prebuild 時に実行したいコマンドを devcontainer.jsononCreateCommand に指定します

"onCreateCommand": "sh /workspaces/drobe/script/codespace/prebuilt.sh"
devcontainer.json 抜粋した onCreateCommand の例

onCreateCommand を書いたら repository の settings から Codespaces → Set up prebuild をクリックします。

Prebuild の設定では Branch を選んで Region と Trigger を選択します。

image
image

以降、選択した branch に push がある度に GitHub actions が自動で走り (ここでは github actions 用 workflow の yml を用意する必要はありません) devcontainer の build → onCreateCommand の実行が終わったものを prebuild image として保存してくれます。

この prebuild を設定する事で、開発開始までの時間は劇的に短縮されます。

弊社の場合は setup の script が 30 分くらい掛かってしまっており、 git clone から開発を開始できるまで、少なくとも 1 時間くらい、ハマったりすると半日以上掛かる事もよくありました。prebuild 無しの codespaces を利用すると、コマンドが途中でこけてハマるというような事はなくなりましたが、それでも新しい環境を用意するのに 30 分掛かっていました。

prebuild を利用した codespace 環境を手に入れてからは、開発に初めて参加する人でも数分以内にはセットアップが完全に終わった新しい環境を用意することが出来るようになりました。

使ってみてどうか?

使ってみた率直な感想としては、単純に最高だなと思っています。数分で確実に新しい環境が手に入るので、ブランチ毎に環境を準備するなども可能で、開発の幅も広がりそうだと感じています。ローカルのマシンで docker を走らせなくて良いというのもスッキリして快適さを感じます。

また、元々持っていた課題に対してはどうだったかというと以下のように全ての課題を解決する事が出来ました 🎊

  1. ⏳ 開発環境のセットアップに時間が掛かる
  2. ✅ 起動は 30 秒 ~ 数分くらいでサッと環境が立ち上がります。(若干 docker の pull 時間に依存して長くなる時がありました)

  3. 🤓 新しく参加される方がセットアップにハマった際に、既存メンバーによるサポートが割と負荷が掛かる
  4. ✅ 今のところ確実に誰でも動作しています

  5. 📑 セットアップのドキュメントを整備していくコストが高い (もし xxx なら yyy をしてください、など注釈が必要になってきて、書く方も読む方も複雑化なものと向き合う必要がある)
  6. ✅ 必要なツールなどがあれば devcontainer をメンテする、という方針にかわりました

  7. 💻 Linter や Formatter が Visual studio code の Extension に依存していて、設定を合わせるのが微妙に面倒
  8. ✅ Linter や Formatter を含めて codespaces に設定出来るので迷わないし自分で設定する必要がない

  9. 👩‍💻 リモートであるがゆえに席に来てもらって開発中の画面を触ってもらう、などが出来ない (アニメーションなど)
  10. ✅ 自分の画面を github の organization のメンバーに好きな時にシェアできる

さいごに

DROBE では一緒にコードを書く仲間を募集しています!

Codespace 気になったからちょっと使ってみたい、とかもウェルカムですので、気になった方は是非お気軽にお知らせください!