Speee DEVELOPER BLOG

Speee開発陣による技術情報発信ブログです。 メディア開発・運用、スマートフォンアプリ開発、Webマーケティング、アドテクなどで培った技術ノウハウを発信していきます!

GitHub Codespacesの導入とその設計

ネイティブアド配信プラットフォームUZOUの開発をしているエンジニアの @kanga333 です。UZOUでは今年の8月あたりからGitHub Codespaces(以後Codespaces)を全面的に導入しました。この記事ではUZOUにおけるCodespacesの利用事例について簡単に紹介します。

GitHub Codespacesとは?

GitHub CodespacesとはGitHubの提供するクラウド開発環境のサービスです。Codespacesを利用するとクラウド上のVMの上にリポジトリのコードと諸々の環境がセットアップされたコンテナが立ち上がり、開発者はその環境に接続して開発をすることができます。


GitHub, Inc. 自体がGitHub Codespcesのユーザーであるため、かなりの大規模環境でも実績のあるサービスとなっています。GitHub自身の事例に興味が出た方は以下の記事を読んでみてください。私もこの記事を読んでCodespacesに興味を持ちました。

github.blog


VSCodeが最もCodespacesに統合されているエディタですが、SSHで接続することができるため、JetBrainsのようなリモート開発機能を持つエディタや、Vim、EmacsなどのCUIベースのエディタでも問題なく開発することができます。

UZOUでの利用背景

Codespacesの導入に至ったのはいくつかの課題背景がありました。


UZOUはコンポーネントごとにGo, Python, Ruby, TypeScript/JavaScriptと多岐に渡る言語で開発されています。ゼロからのセットアップというのはそう頻繁に行われるものではないので、コードベースの変化に伴いセットアップ手順が現状と乖離してしまい、新規入社者が特に不慣れな言語においてセットアップにハマってしまうという課題がありました。これではオンボーディングに時間がかかってしまうので望ましい状況とは言えません。


また、Apple Siliconの登場に伴い、Macで開発するために開発環境をArmアーキテクチャで動かせるようにする必要がありました。しかしUZOUで利用しているいくつかのライブラリやミドルウェアは当時Armアーキテクチャ未対応であったため*1、最新のMacで開発を継続するためには何らかの対応をする必要がありました。


これらの課題を解決する方法の一つとしてGitHub Codespacesを検証し、導入した運びとなります。

設計

Codespacesを導入するためには、Codespacesの設定ファイルとなる devcontainer.json 、コンテナイメージをカスタマイズする場合はその Dockerfile 、Codespaces環境でも動く、テストやアプリケーション起動のための、スクリプトや手順を整備する必要があります。


将来的にはApple SiliconのローカルPCでの開発もできるようしたいので、ローカルでの開発フローとCodespacesでの開発フローになるべく差分が出ないような設計となることを意識しました。


ここからはUZOUにおいて、Codespaces環境の設計をどうしていったか具体的なTipsについて述べます。

Codespaces環境用のカスタムイメージを用意しBuildKitとGHCRでビルドを高速化する

Codespacesはデフォルトで全部入りみたいなコンテナの開発環境を提供してくれますが、今回は自分達のコードベースに合った開発環境を用意するために自前でコンテナイメージを用意して利用する方針としました。


自前のイメージはデフォルトブランチにマージがされる度にGitHub ActionsでGHCRにPushするようにし、Codespaces起動時にはそのイメージをBuildKitのリモートキャッシュとして使いつつ、毎回ビルドする設計にしました。ちなみにGHCRはGitHub ActionsやCodespacesからクレデンシャルを渡さなくても使えて便利です。


コードで具体例を示すと、こんな感じです。

 # GitHub ActionsでCodespacesを使うイメージをビルドしcache-toでビルドキャッシュをGHCRに送る
docker buildx build \
  -f path/to/Dockerfile \
  --tag ghcr.io/speee/${repository_name}$:latest \
  --cache-from=ghcr.io/speee/${repository_name}:cache-latest \
  --cache-to type=registry,ref=ghcr.io/speee/${repository_name}:cache-latest,mode=max \
  --push


devcontainer.json抜粋

// build命令でcacheFromオプションをつける
{
    "build": { 
        "context": "..",
        "dockerfile": "../path/to/Dockerfile",
        "cacheFrom": "ghcr.io/speee/${repository_name}/ci:cache-latest"
    }
}


この設計はCodespaces側のDockerfileやAdd/Copyしているファイルに変更が無い限り、イメージの各レイヤのチェックサムがGHCRにあるイメージのチェックサムと一致するため、実質イメージをPullするだけでCodespacesの環境が立ち上がります。Dockerfileを編集した場合は、チェックサムが一致しなくなるので、不一致のレイヤ以降のレイヤが再ビルドされるようになります。この挙動により、GHCRにイメージをPushしなくても新しいDockerfileを元にしたCodespaces環境の作り直しが検証できます。

余談

この設計を実装するにあたり、コードに何の変化も入れてないのにCodespaces環境作成時のdocker buildにおいて、AddやCopy命令以後のチェックサムがGHCR側のイメージと必ずずれてしまうという事象が発生しました。


もろもろ調べた結果、CodespacesはACLによるファイルパーミッションの制御を行っているので、それによってCodespaces側でBuildする時にファイルパーミッションがずれてしまっていたというのが原因だと判りました。*2


現在はGitHub ActionsでイメージをBuildしてPushする際に、Codespacesでビルドする時と同じようなファイルパーミッションに変更してビルドするフローに変更することでこの問題を回避しています。

Codespacesから使うDockerはDocker in Dockerで実現

テストに使うMySQLなどの立ち上げなど、Dockerはモダンな開発において必須のツールです。Codespacesで立ち上がる環境はコンテナであるため、コンテナ内からDockerを使えるようにする必要があります。コンテナ内からDockerを使う方法は主にDocker in DockerとDocker outside of Dockerと呼ばれる2つの方法があります。

  • Docker in Docker (dind)
  • Docker outside of Docker (dood)
    • ホストマシンのdocker.sockを子コンテナにマウントしコンテナ内からはsock経由でホストマシンのDocker daemonとやり取りをしてDockerを使う方法
    • コンテナ内からdockerコマンドを簡単に実行できるようにはなるが、子コンテナとホストマシンのファイルシステム空間が異なるため、マウントオプションの取り扱いに難がある


dindであれば、ローカルのPCから直接Dockerを使う場合と同じ操作感でDockerを使うことができます。操作手順などの差異をローカルとCodespaces間で発生しないようにするため、dindの方式を採用しました。


また、MySQLなどの開発に必要なコンテナはdocker composeで立ち上げるようにし、Codespacesでもローカルでも同じような開発体験になるような構成にしています。

Secretsの管理にはAWS SSOとSystem Manager Parameter Storeを使う

秘匿情報をCodespaces内で使いたい時はaws ssoを使ってIDaaD経由でawsの認証を済ませ、スクリプト経由でaws ssmコマンドを使って秘匿情報をexportするフローとしています。これによってGitHub側に環境変数による秘匿情報を一切設定することなく、秘匿情報を開発時に扱えるようにしています。これを実現するためCodespaces環境には事前にaws-cliコマンドをインストールしておき、AWS SSO*3するための設定ファイルも配置しておきます。


また開発時に検証環境のDBのデータを使いたい場合などのケースではaws ssm start-sessionコマンドを使ってAWSに接続できるようにしています。


ちなみにCodespacesはGitの認証をHTTPS認証にすれば追加のクレデンシャルなど無しに、他のプライベートリポジトリをクローンすることができます。*4submoduleとかが使いやすくて便利。GitHub Actionsにもこの機能欲しい。

導入の成果

ここからはCodespacesを導入してどうだったか、という話について記載します。

Apple SiliconeのMacを使えるようになった

Codespacesにより、引き続きx86_64の環境で開発できるようになったので*5、Apple SilicornのMacに端末を変えれるようになりました。直近数ヶ月でUZOUの開発に新規で参画したメンバーに、最新スペックのMacをお渡しできて良かったです。今更Intel CPU(x86_64)のMac渡されるとかツラい。自分もこっそり最新のM2 Macに端末を変更しました。*6


将来的には開発環境のArm対応を済ませてローカルとCodespacesどちらでも開発できるようにしていきます。

開発環境を複製可能になった

PullRequestのレビュー依頼を受けた際に、Codespacesを使えばレビュー依頼を受けたブランチを元に独立した環境を新規に別で作ることができます。そのため自分の手元にある開発中のブランチを退避させたりする作業が必要がなくなりました。レビューをする際は、新規でそのブランチのCodespacesを作って、必要であれば動作確認をし、終わったらその環境を消す、というシンプルなフローが実現できます。これは特にバージョンアップなどの環境が大きく変わるPullRequestのレビューなどにおいて便利です。

セットアップ手順が陳腐化しにくくなった

開発環境を作るという行為が自動化され、Immutableになった結果、開発環境をゼロから作り直すという行為のコストが下がりました。それに伴い開発環境をゼロから何度も作り直すというのが通常の開発フローに組み込まれたため、セットアップの手順や運用が現状のコードベースと乖離する、という事が起きにくくなったのではと感じています。まだ導入して半年も経っていないので実際の所はもう少し年月経ってみないとわからないかもしれません。

オンボーディングコストの減少

初めてリポジトリを触ってもらう時でも、Codespaces立ち上げて、make testすればテスト動いてmake upでアプリケーションが立ち上がる、という動く環境が直ぐ手に入る状況になったので、オンボーディングコストを減らす事ができました。秘伝のたれセットアップ手順を一生懸命説明して、それが思ったように動かなくて動かなくて悲しい、みたいな悲劇を減らせます。

CPU負荷のオフロードができる

ローカルPCと分離したマシンで開発できるため、例えば重いビルドやテストをぶん回している最中でもローカルPCが影響を受けることが無くなりました。テストを流しながらZoomのミーティングをしても快適です。

終わりに

以上、UZOUにおけるGitHub Codespacesの導入事例について簡単に説明させて頂きました。


私自身、Apple SilicornのMacにして以来、プライベートの開発含めて全てをCodespacesで賄っていますが、特段大きな不便を感じていません。それどころか環境の破棄容易性やリポジトリ間でライブラリの依存が完全に分離できる点が気に入ってます。


またCodespacesはOSS活動においても、有用だなと感じています。特に大規模なOSSで貢献するための障壁の一つに、動作させるための環境を用意するのが大変ということがあります。Codespacesを使い1クリックで動作する環境を提供することができれば、Contributeの障壁を低くすることができます。私の知ってる具体例ではApache AirflowのプロジェクトでCodespacesをサポートしています


一方で手放しでCodespacesが全て良いという訳ではありません。ローカルでの開発と比較すると立ち上がりに時間がかかる点などのデメリットもあります。


今後、UZOUでもしっかりとArm対応を済ませて、ローカル、Codespacesを状況に合わせて使い分けれる環境を用意していけたらと思っています。

UZOUでの開発に興味が出た!または俺がArm対応やったるぜ!!という方は是非採用ページをチェックしてみてください。


こちらのFormよりカジュアル面談も気軽にお申し込みいただけます!



※この記事は、2022 Speee Advent Calendar14日目の記事でした。 昨日のそめじさんのOSSの記事も是非チェックしてね。

tech.speee.jp


*1:UZOUではAerospikeという分散KVSを使っており、当時AerospikeのArm対応が未定だったのが大きなネックでした、2022年11月リリースされた6.2.0よりArmで動かせるようになってます

*2:https://github.com/orgs/community/discussions/26026

*3:AWS SSOさんは今はAWS IAM アイデンティティセンターという名前、でもコマンドはaws ssoのまま

*4:もちろんユーザーがそのリポジトリに対するアクセス件を持っている場合に限ります

*5:現時点でCodespacesはx86_64のアーキテクチャの環境のみを提供しているように見えますが、調べた限りマシンスペックに関する文章は見当たりませんでした

*6:Speeeでは福利厚生の一つに2年に1回最新スペックのMacに変更できるという制度があります https://www.engineer.speee.jp/