はじめに
Canonical から Workshop というツールが公開されました。
Workshop は、プロジェクトごとに必要な開発環境を定義し、LXD ベースの sandbox として起動できる開発環境構築ツールです。
今回、自作の RISC-V 64-bit microkernel プロジェクトである Rk-C に Workshop を導入してみました。
GitHub: https://github.com/shizuku198411/Rk-C
Rk-C は Nim を中心に書いている実験的な microkernel で、QEMU virt + OpenSBI 上で動作します。
このような低レイヤ系プロジェクトでは、開発環境に必要な依存関係がかなり多くなりがちです。
例えば Rk-C では、少なくとも以下が必要になります。
- Nim
- QEMU RISC-V system emulation
- OpenSBI
- RISC-V GNU toolchain
- clang / lld / LLVM tools
- Python 3
- GNU make
- QEMU 上での smoke test
- GitHub Actions との連携
これらをホスト環境に直接入れていくと、環境差分がかなり出やすくなります。
Workshop を使うことで、この複雑な開発環境をプロジェクト側に定義として持たせられるようになりました。
Workshopとは
Workshop は、プロジェクト用の開発環境を定義して起動するためのツールです。
ざっくり言うと、次のような情報を yaml で定義します。
- Ubuntu の base
- 利用する SDK
- プロジェクトローカルのセットアップ
- よく使うコマンド
- tunnel や mount などの interface
そして、定義した環境を workshop launch で起動し、workshop run でコマンドを実行できます。
例えば、プロジェクト側で build や test という action を定義しておけば、開発者は次のように実行できます。
workshop run rkc-dev -- build
workshop run rkc-dev -- test
重要なのは、これらのコマンドがホスト上で直接実行されるのではなく、Workshop が用意した sandbox 環境内で実行されることです。
これにより、ホスト環境に依存しにくい、再現性のある開発環境を作りやすくなります。
基本的な使い方
まず、ホスト側には LXD と Workshop を入れます。
sudo snap install --channel=6/stable lxd
sudo snap install --classic workshop
プロジェクト内に Workshop の定義を置きます。
単一の Workshop だけなら workshop.yaml を置く形でもよいですが、複数の Workshop を使う場合は .workshop/ 以下に定義を置きます。
今回の Rk-C では次のような構成にしています。
.workshop/
rkc-dev.yaml # workshop #1
rkc-actions.yaml # workshop #2
rkc-base/ # project local SDK #1
act-base/ # project local SDK #2
Workshop を起動します。
workshop launch rkc-dev
状態を確認します。
workshop list
定義済みの action を実行します。
workshop run rkc-dev -- build
workshop run rkc-dev -- test
workshop run rkc-dev -- run
Rk-Cでの利用例
Rk-C では、Workshop を2つに分けています。
rkc-dev
Nim / QEMU / OpenSBI / RISC-V toolchain を含む実開発環境
rkc-actions
Docker / act を使った GitHub Actions workflow のローカル確認環境
このように分けた理由は、実際の OS 開発環境と、workflow の検証環境を混ぜたくなかったためです。
rkc-dev
rkc-dev は、Rk-C のビルド、実行、テスト、デバッグを行うメインの開発環境です。
定義のイメージは次のようになります。
name: rkc-dev
base: ubuntu@24.04
sdks:
- name: nim-toolchain
channel: latest/stable
- name: project-rkc-base
- name: system
plugs:
gdb:
interface: tunnel
endpoint: localhost:1234
nim-toolchain は、Nim 言語の toolchain を提供する SDK です。
また、project-rkc-base は Rk-C 用のプロジェクトローカル SDK で、QEMU、OpenSBI、LLVM tools など、Rk-C の開発に必要な環境を準備します。
よく使う操作は action として定義しています。
actions:
build: |
set -eux
cd /project
make build
build-bins: |
set -eux
cd /project
make build-bins
test: |
set -eux
cd /project
make test-apps TEST_APPS_ARGS="--boot-timeout 60 --command-recover-timeout 30 --qemu-net user --skip-network-smoke"
run: |
set -eux
cd /project
make run
debug: |
set -eux
cd /project
make qemu-debug
clean: |
set -eux
cd /project
make clean
これにより、ローカルでの操作はかなりシンプルになります。
workshop run rkc-dev -- build
workshop run rkc-dev -- run
workshop run rkc-dev -- test
QEMU や OpenSBI、Nim のインストール手順を毎回ホストに合わせて調整する必要がなくなり、プロジェクトが想定する環境をそのまま起動できるようになりました。
rkc-actions
もう1つの Workshop として rkc-actions を用意しています。
これは GitHub Actions の workflow をローカルで確認するための環境です。
当初は Workshop の GitHub runner 連携も検討しました。
ただし、その方式は self-hosted runner を起動する形になるため、public repository で使う場合は、自分のローカル環境に外部から workflow job が流れてくる可能性があります。
今回の用途では、外部から job を受ける必要はなく、単に workflow の構文や分岐がローカルで確認できれば十分でした。
そのため、act を使う構成にしました。
rkc-actions のイメージは次のようになります。
name: rkc-actions
base: ubuntu@24.04
sdks:
- name: docker-ce
channel: latest/stable
- name: project-act-base
actions:
list: |
set -eux
cd /project
act -l
check-workflow: |
set -eux
cd /project
act pull_request -j test-apps
docker-ce SDK により、Workshop 内で Docker を利用できます。
project-act-base では act をインストールしています。
これにより、次のように GitHub Actions workflow をローカルで確認できます。
workshop run rkc-actions -- list
workshop run rkc-actions -- check-workflow
ここでの目的は、QEMU を使った実テストを完全に再現することではありません。
rkc-actions では、主に以下を確認します。
- workflow の構文
- event の指定
- job ID の指定
-
ACT環境変数による分岐 - checkout step
- artifact upload step
一方で、実際の QEMU smoke test は rkc-dev 側で実行します。
workshop run rkc-dev -- test
この分離により、rkc-dev は純粋な開発・実行環境、rkc-actions は workflow 検証環境として扱えるようになりました。
GitHub Actionsでの利用
Workshop は GitHub Actions 上でも利用できます。
Rk-C では、GitHub Actions 本番 CI では rkc-dev を起動してテストを実行しています。
複数 Workshop を持つプロジェクトでは、canonical/launch-workshop にどの Workshop を起動するか明示する必要があります。
- name: Launch Workshop
if: ${{ !env.ACT }}
uses: canonical/launch-workshop@v0
with:
token: ${{ secrets.WORKSHOP_TOKEN }}
workshop: rkc-dev
- name: Test on Workshop
if: ${{ !env.ACT }}
run: workshop run rkc-dev -- test
rkc-actions で act を使ってローカル確認する場合は、重い QEMU smoke test はスキップしています。
- name: Skip Workshop test in local act
if: ${{ env.ACT }}
shell: bash
run: |
set -eux
echo "Skipping Workshop-based QEMU smoke test under local act."
echo "This local act run validates the workflow wiring only."
echo "Run the actual test with: workshop run rkc-dev -- test"
この構成により、次の3つのモードを分けられます。
ローカル実テスト:
workshop run rkc-dev -- test
ローカルworkflow確認:
workshop run rkc-actions -- check-workflow
本番CI:
canonical/launch-workshop@v0 で rkc-dev を起動して test
Docker / Docker Compose と違う点
Workshop は、Docker や Docker Compose と似た用途に見える部分もあります。
どちらも「環境を定義して、ホスト環境との差分を減らす」という意味では近いです。
ただし、Workshop は Docker Compose の置き換えというより、プロジェクト用の開発環境を定義して、操作手順まで含めて扱うためのツールという印象です。
Docker Compose はサービス構成を定義する
Docker Compose は、複数のコンテナを組み合わせたアプリケーション構成を定義するのに向いています。
例えば、Web アプリケーションであれば次のような構成です。
app container
database container
redis container
worker container
このようなサービス群を起動し、ネットワークや volume をつなぐ用途では Docker Compose は非常に強力です。
一方で、Rk-C のようなプロジェクトでは、主な対象は常駐サービス群ではありません。
必要なのは、次のような「開発用 toolchain 環境」です。
Nim
QEMU
OpenSBI
RISC-V toolchain
LLVM tools
Python scripts
Makefile targets
GitHub Actions workflow checks
つまり、動かしたいのは Web サービス構成というより、ビルド・実行・テストのための開発環境です。
Workshop は action をプロジェクトの入口にできる
Workshop では、環境だけでなく、よく使う操作を actions として定義できます。
例えば Rk-C では、次のような action を定義しています。
actions:
build: |
set -eux
cd /project
make build
test: |
set -eux
cd /project
make test-apps TEST_APPS_ARGS="--boot-timeout 60 --command-recover-timeout 30 --qemu-net user --skip-network-smoke"
run: |
set -eux
cd /project
make run
これにより、利用者は内部の依存関係を意識せずに、次のようなコマンドで作業できます。
workshop run rkc-dev -- build
workshop run rkc-dev -- test
workshop run rkc-dev -- run
個人的には、この「環境定義」と「よく使う操作」が同じ場所にまとまる点がかなり気に入っています。
SDK として開発環境を分けられる
Workshop では SDK という単位で toolchain やセットアップを分けられます。
Rk-C では、Nim toolchain 用の SDK、Rk-C 固有の SDK、Docker / act 用の SDK を組み合わせています。
rkc-dev
nim-toolchain
project-rkc-base
rkc-actions
docker-ce
project-act-base
このように、複数の Workshop を作り、それぞれに必要な SDK だけを入れられます。
Docker Compose でも複数サービスを分けられますが、Workshop の場合は「開発用途ごとに toolchain 環境を分ける」感覚に近いです。
LXD ベースの Ubuntu 環境として扱える
Workshop は LXD を利用して Ubuntu ベースの sandbox を起動します。
そのため、Docker image を作るというより、Ubuntu 環境の中に project-specific な toolchain を用意する感覚に近いです。
Rk-C のように、QEMU、OpenSBI、RISC-V toolchain、system package、snap、Docker などが絡む場合、Ubuntu 環境として扱えるのはかなり便利でした。
もちろん Docker / Docker Compose でも同じような環境を作ることは可能です。
ただ、Workshop は最初から「開発環境」「SDK」「action」「project directory」という単位で設計されているため、開発者向けの入口を作りやすいと感じました。
使い分けのイメージ
個人的には、次のように使い分けると分かりやすいと思います。
Docker / Docker Compose
アプリケーションやサービス群をコンテナとして起動するための構成管理
Workshop
プロジェクト固有の開発環境、toolchain、よく使う操作をまとめるための環境管理
Web アプリケーションのサービス構成を立ち上げるなら Docker Compose が向いています。
一方で、OS、kernel、embedded、compiler、emulator、toolchain 系のように、開発環境そのものが複雑なプロジェクトでは Workshop がかなり合うと感じました。
Workshopを使ってよかったところ
今回 Workshop を使って特によかったのは、開発環境がプロジェクトの一部になったことです。
Rk-C のような低レイヤ系プロジェクトでは、依存関係が多く、ホスト環境によって差分が出やすいです。
特に以下のようなものが絡むと、セットアップ手順が長くなりがちです。
- QEMU
- OpenSBI
- RISC-V toolchain
- Nim
- LLVM tools
- smoke test scripts
- GitHub Actions workflow
Workshop によって、これらをプロジェクトの定義としてまとめられました。
その結果、開発者が最初に触るコマンドはかなりシンプルになります。
workshop launch rkc-dev
workshop run rkc-dev -- test
また、複数 Workshop を使うことで、開発環境と workflow 検証環境を分けられた点も便利でした。
rkc-dev
実際の開発・ビルド・QEMU実行・テスト
rkc-actions
Docker + act による workflow wiring の確認
依存関係が多いプロジェクトほど、Workshop のメリットは大きいと感じました。
まとめ
Canonical Workshop を使うことで、Rk-C の複雑な開発環境をかなり整理できました。
特に良かった点は次の通りです。
- プロジェクトごとに開発環境を定義できる
- Nim / QEMU / OpenSBI / RISC-V toolchain などをまとめられる
- ローカルと CI で近い環境を使える
- 複数 Workshop により用途ごとに環境を分けられる
-
actと組み合わせて GitHub Actions workflow もローカル確認できる
Workshop はまだ新しいツールですが、低レイヤ開発、組み込み、エミュレータ、toolchain 系のプロジェクトとはかなり相性が良いと感じました。
Rk-C では、Workshop によって開発環境がかなり再現しやすくなり、プロジェクト全体の見通しも良くなりました。