しばやん雑記

Azure とメイドさんが大好きなフリーランスのプログラマーのブログ

GitHub Codespaces と Prebuilding Codespaces を使ってサクサク起動する Miniconda の環境を構築する

最近は C# と Node.js 以外の環境は WSL 2 や GitHub Codespaces を使って用意することが多いのですが、Conda (Miniconda) を使った環境の構築時にかなりハマったのでメモとして残します。

結果的にはまずまずの環境が出来たと思っていますが、思ったより情報が少ないのと検証中は Codespace のリビルドに時間がかかってしまい厳しかったです。

例によって GitHub Codespaces と言っていますが、中身は Dev Container なのでローカルの VS Code でも問題なく使えるはずです。ちなみに GitHub Codespaces 自体は Team か Enterprise を契約しているとすぐに使えますが、個人向けは永遠にベータの予感がしてきました。

Miniconda の Dev Container 定義を作成する

作成したばかりの空っぽのリポジトリでも汎用的なイメージを利用する Codespace を作成できるので、Dev Container のセットアップのためにリポジトリをローカルに git clone してくる必要すらありません。

個人向けの Codespace ではマシンスペックを選べませんが、Team / Enterprise を契約していると 4 種類から選べるので最適なものを選びます。Codespace 内で Docker Image のビルドを行う場合は 32GB ストレージだと心許ない場合もあるので、ストレージ容量も気にしておきます。

f:id:shiba-yan:20220307231256p:plain

汎用イメージの Codespace 環境が立ち上がったら Miniconda の Dev Container 定義をテンプレートを利用して作成していきます。このテンプレートの良し悪しはともかくとして、とっかかりとしては最適です。

f:id:shiba-yan:20220307225335p:plain

テンプレートの定義は GitHub 上で公開されているので README が参考になります。

Dev Container 定義を作成して、Codespace のリビルドを行うと Miniconda 環境が立ち上がります。ここまで全てブラウザだけで完結していて、所要時間もほんの数分です。

Miniconda の仮想環境をカスタマイズする

テンプレートから作成した Miniconda の環境は完全にまっさらの状態で、仮想環境もデフォルトの base のみ有効になっているという状態です。ここから必要に応じて仮想環境をカスタマイズしていきます。

f:id:shiba-yan:20220307225349p:plain

仮想環境を構築するために必要なパッケージ類は yml に書いておけばよいです。この時 Python 自体を入れてバージョンを固定するのが良さそうでした。

とりあえずサンプルとして OpenCV を使うコードを動かしてみたいので、Python のバージョン固定と pip で OpenCV をインストールする定義を作成しておきました。

この程度なら Conda 使わなくても十分なのですが、ぶっちゃけ良いサンプルを思いつかなかったです。

name: default
channels:
  - conda-forge
  - default
dependencies:
  - python==3.9.*
  - pip
  - pip:
    - opencv-python-headless

ここであえて conda-forge にある opencv を使わずに pip にある opencv-python-headless を使っているのは、Codespace というか Docker では libGL.so.1 が見つからないエラーになるためです。

テンプレートから作成した Miniconda の Dev Container では environment.yml というファイル名でリポジトリのルートに置いておけば、Codespace のリビルド時に自動的に使われるようになっています。

f:id:shiba-yan:20220307225653p:plain

この状態で Codespace のリビルドを実行すると base に対して environment.yml の内容が反映されます。パッケージの数に比例して Codespace のビルド時間が伸びてくる傾向にあります。

f:id:shiba-yan:20220307231745p:plain

既存の仮想環境 base に反映するのではなく、新規に作りたい場合は .devcontainer ディレクトリ内にある Dockerfile を修正すると良いです。具体的には conda env update ではなく conda env create に修正すると新しい仮想環境が作成されます。

ターミナル起動時に仮想環境をアクティブ化する

ここまでで Codespace で作成された Miniconda 環境は environment.yml の定義を基にカスタマイズ可能になりましたが、ターミナルを起動すると仮想環境がアクティブ化されていないという罠があります。

これは Conda の初期化処理が Codespace で利用する shell に登録されていないことが原因なので、以下のように postCreateCommandconda init bash を実行しておくとアクティブ化されるようになります。

// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at:
// https://github.com/microsoft/vscode-dev-containers/tree/v0.222.0/containers/python-3-miniconda
{
	"name": "Miniconda (Python 3)",
	"build": { 
		"context": "..",
		"dockerfile": "Dockerfile",
		"args": {
			"NODE_VERSION": "none"
		}
	},

	// Use 'postCreateCommand' to run commands after the container is created.
	"postCreateCommand": "conda init bash"
}

これで Codespace をリビルドすると、ターミナルを起動すると自動的に base がアクティブ化されます。

f:id:shiba-yan:20220307225404p:plain

手動でアクティブ化したい場合には postCreateCommandconda init bash && conda config --set auto_activate_base false などと書いておけば、Conda に必要な初期化をしつつ base は自動で base アクティブ化しない構成に出来ます。

OpenCV のサンプルコードを適当に用意する

ここまでのカスタマイズで Codespace 上で Python と OpenCV が利用可能になっているので、適当に画像処理を行うコードを用意して動作確認しておきます。グレースケール化するシンプルなものです。

import cv2

source_image = cv2.imread('shibayan.png')

gray_image = cv2.cvtColor(source_image, cv2.COLOR_BGR2GRAY)

cv2.imwrite('output.png', gray_image)

実行に成功すると output.png が生成されるので、開くとグレースケール化された画像が確認出来ます。

f:id:shiba-yan:20220307231554p:plain

ここまで作成した Dev Container 定義をコミットしておけば、次からは全員が同じ環境で Codespace を使い始めることが出来ます。開発環境を簡単に統一できるので最高に便利です。

Prebuilding Codespaces を利用して起動時間を高速化する

インストールするパッケージが多くなると Codespace のビルドに非常に時間がかかるためストレスが溜まってしまうのですが、最近プレビューになった Prebuilding Codespaces を使うと不満を解消できます。

GitHub 自体の開発環境は同じようにビルド済みのイメージが使われているので数十秒で起動するらしいですが、今回の Prebuilding Codespaces はそれが一般公開されたものと考えることが出来ます。

設定自体は非常に簡単でビルドを行うブランチと、ビルドされたイメージをアップロードするリージョンを選択するだけです。今後はアップロードに課金されるようになるので、必要最小限にしておくのが良いです。

f:id:shiba-yan:20220307231323p:plain

Prebuilding Codespaces の設定を追加すると、GitHub Actions としてビルド処理が実行されます。

ビルドのトリガーはブランチが更新されたタイミングになるので、常に最新の状態にアップデートされます。Workflow 定義は作成されず、完全に GitHub 側で管理されています。

f:id:shiba-yan:20220307231331p:plain

ビルドが完了すると Codespace の新規作成画面に "⚡Prebuild ready" というラベルが表示されます。

f:id:shiba-yan:20220307232044p:plain

この状態で新しい Codespace を作成するとビルド済みのイメージが利用されるので、数十秒で Codespace が利用可能になります。毎回ビルドするのに比べると圧倒的なスピードで起動します。

f:id:shiba-yan:20220307232241p:plain

プレビューの時点でかなりメリットがある機能ですが、今のところは新規作成時のみビルド済みのイメージが使われるようで、既存の環境はブランチを最新にしてリビルドを行ってもビルド済みのイメージは使われませんでした。今後はもっと再利用されるのを期待したいです。

後半は Miniconda と関係のない Prebuilding Codespaces の話でしたが、ビルドに時間のかかる環境程メリットが大きいので組み合わせて利用していきたい機能です。