この記事はCyberAgent Developers Advent Calendar 2021 10日目の記事です。

昨日はみーとみさんの「待機児童問題にマーケットデザインで挑む」でした。

はじめに

CIU (CyberAgent group Infrastructure Unit) の中西 (@whywaita) です。 普段はプライベートクラウドであるCycloudのIaaSから上のレイヤーを中心に開発運用を担当しています。

直近ではGitHubからリリースされているGitHub ActionsというCIサービスに関連したmyshoesというソフトウェアを開発しています。

myshoesはOSSで公開されており、直近でもいくつかmyshoesについて登壇させていただいたので興味のある方はぜひこちらをご覧ください。

ちなみにmyshoesの初お披露目は去年のCyberAgent Developers Advent Calendarでした。時が経つのは早いですね……。

いきさつ

myshoesは一言で言うならば「GitHub Actionsのrunnerを動的に管理してくれるくん」です。GitHub Actionsで実行するジョブを検知し動的にインスタンスを作成、そのインスタンス上でGitHub Actionsから提供されたworkflowを実行します。

GitHub Actionsからジョブを受け取る処理はGitHubが公開しているactions/runnerというソフトウェアを利用しています。actions/runnerは現在Linux, macOS, Windowsに対応しており、これらのOS上であれば気軽にインストールし起動させることができます。これをGitHub Actions用語で「self-hosted runner」と呼び、GitHubが管理するrunnerを「GitHub-hosted runner」と呼びます。

また、GitHub ActionsにはGitHub Marketplace上で多くのActionsが公開されています。これらを用いることで複数のWorkflowで「よくやるスクリプト」を共通化することが可能になります。 利用例の多いところはプログラミング言語のセットアップ系(setup-go, setup-python, setup-dotnet など)やGitHub Pagesへのデプロイを簡単に行えるpeaceiris/actions-gh-pagesなどがあります。 これらのActionsを組み合わせることで簡単に操作を行えるようになります。

self-hosted runnerでもこれらのActionsを利用することができるのですが、いくつかのActionsはGitHub-hosted runnerを前提としていることがあります。前述のsetup-pythonでは、特定のディレクトリが存在し環境変数が正しく設定されていることを前提としていたり、self-hosted runnerではシンボリックリンクが必要となることがあります。

setup-pythonのようなGitHubが公式に提供しているActionでもこのような実装が存在しており、ユーザ提供のActionsでも類似の事象が発生することが予想されました。 実際にmyshoesを用いてGitHub Actionsのrunnerを提供するにあたりプライベートベータを実施していたのですが、当初はプレーンなUbuntuを利用していたものの「このActionsが利用できない」などの問い合わせを多くいただき、適宜対応していました。

しかし、setup-pythonのように公式提供ActionだけでなくMarketplaceでユーザが作成していたものも含めて全てを対応することは現実的には不可能です。 どのツールが重要である、どのライブラリが無いと困るなどの判断を毎回行うことに対するコストも高く感じました。

そこで、myshoesで用いるOSイメージをGitHub-hosted runnerと同じものにするアイデアを思いつきました。今回は実際にこれらのイメージを生成する方法についてご紹介します。

本題

GitHub-hosted runnerはバックエンドにAzureを用いていることが知られています1

そして、Azure(のArmインスタンス)向けのpacker定義ファイルがactions/virtual-environmentsとして公開されています。これを利用することでAzure向けのGitHub-hosted runer OSイメージを自分で生成することができます。 しかし残念ながら我々はAzure提供者ではありません。そのため、自分たちの都合の良いクラウド基盤上で動作するように書き換えてあげる必要があります。

我々はLXDを用いてサービスを提供しています。軽量なVMMである点、それぞれのインスタンスでdockerdが動作する点、APIによって操作できる点などから採用しています。 今回はLXD向けのpacker定義ファイルを用意したいと思います。

早速ですが成果物がこちらです。

whywaita/virtual-environments-lxd: GitHub Actions virtual environments For LXD

whywaita/virtual-environments-lxd は lxd.patch というパッチファイルがメインコンテンツです。これはその名の通りactions/virtual-environmentsのmainブランチに対してLXDの対応を導入するパッチです。

実際にビルドしてみましょう。事前にLXDとpackerはそれぞれインストール済みの環境を想定しています。

# virtual-environments-lxd と virtual-environments をcloneする
$ git clone https://github.com/whywaita/virtual-environments-lxd
$ cd virtual-environments-lxd
$ git clone https://github.com/actions/virtual-environments
$ cd virtual-environments

# パッチを適用する
$ patch -p1 < ../lxd.patch

# Ubuntu 20.04 のイメージを生成する
$ packer build images/linux/ubuntu2004.json

このまま2時間ほど待つことでイメージを生成されます。

LXD対応のためにおこなったこと

LXD対応するためにいくつかの対応を行っているため、ピックアップしてご紹介します。

https://github.com/whywaita/virtual-environments-lxd/blob/075f07913c48c22a80be532a1bbaa342c9da1703/lxd.patch#L53-L88

diff --git a/images/linux/ubuntu2004.json b/images/linux/ubuntu2004.json
index 0d70fc4d..f59d3190 100644
--- a/images/linux/ubuntu2004.json
+++ b/images/linux/ubuntu2004.json
@@ -31,29 +31,8 @@
     ],
     "builders": [
         {
-            "type": "azure-arm",
-            "client_id": "{{user `client_id`}}",
-            "client_secret": "{{user `client_secret`}}",
-            "subscription_id": "{{user `subscription_id`}}",
-            "tenant_id": "{{user `tenant_id`}}",
-            "location": "{{user `location`}}",
-            "vm_size": "{{user `vm_size`}}",
-            "resource_group_name": "{{user `resource_group`}}",
-            "storage_account": "{{user `storage_account`}}",
-            "build_resource_group_name": "{{user `build_resource_group_name`}}",
-            "temp_resource_group_name": "{{user `temp_resource_group_name`}}",
-            "capture_container_name": "images",
-            "capture_name_prefix": "{{user `capture_name_prefix`}}",
-            "virtual_network_name": "{{user `virtual_network_name`}}",
-            "virtual_network_resource_group_name": "{{user `virtual_network_resource_group_name`}}",
-            "virtual_network_subnet_name": "{{user `virtual_network_subnet_name`}}",
-            "private_virtual_network_with_public_ip": "{{user `private_virtual_network_with_public_ip`}}",
-            "allowed_inbound_ip_addresses": "{{user `allowed_inbound_ip_addresses`}}",
-            "os_type": "Linux",
-            "image_publisher": "canonical",
-            "image_offer": "0001-com-ubuntu-server-focal",
-            "image_sku": "20_04-lts",
-            "os_disk_size_gb": "86"
+          "type": "lxd",
+          "image": "ubuntu:focal"
         }
     ],

packer の azure-arm builderを LXD builderに切り替えています。 元となっているのはLXD公式イメージの ubuntu:focal なので、どなたでもこのイメージをビルドすることが出来ます。また、OSバージョンに依存した処理を書かないことでこのタグを書き換えることでOSバージョンも切り替えられる(はず)です。

https://github.com/whywaita/virtual-environments-lxd/blob/075f07913c48c22a80be532a1bbaa342c9da1703/lxd.patch#L89-L103

@@ -96,12 +75,12 @@
         {
             "type": "file",
             "source": "{{template_dir}}/scripts/helpers",
-            "destination": "{{user `helper_script_folder`}}"
+            "destination": "{{user `image_folder`}}"
         },
         {
             "type": "file",
             "source": "{{template_dir}}/scripts/installers",
-            "destination": "{{user `installer_script_folder`}}"
+            "destination": "{{user `image_folder`}}"
         },
         {
             "type": "file",

azure-arm builderと lxd builderでファイルパスの取り扱いが異なる実装差異が存在しており、その対応のため利用するファイルパスを切り替えています。

https://github.com/whywaita/virtual-environments-lxd/blob/075f07913c48c22a80be532a1bbaa342c9da1703/lxd.patch#L1-L14

diff --git a/images/linux/scripts/installers/configure-environment.sh b/images/linux/scripts/installers/configure-environment.sh
index d3c99fcf..fe74e829 100644
--- a/images/linux/scripts/installers/configure-environment.sh
+++ b/images/linux/scripts/installers/configure-environment.sh
@@ -12,9 +12,9 @@ mkdir -p /etc/skel/.config/configstore
 echo 'XDG_CONFIG_HOME=$HOME/.config' | tee -a /etc/environment
 
 # Change waagent entries to use /mnt for swapfile
-sed -i 's/ResourceDisk.Format=n/ResourceDisk.Format=y/g' /etc/waagent.conf
-sed -i 's/ResourceDisk.EnableSwap=n/ResourceDisk.EnableSwap=y/g' /etc/waagent.conf
-sed -i 's/ResourceDisk.SwapSizeMB=0/ResourceDisk.SwapSizeMB=4096/g' /etc/waagent.conf
+#sed -i 's/ResourceDisk.Format=n/ResourceDisk.Format=y/g' /etc/waagent.conf
+#sed -i 's/ResourceDisk.EnableSwap=n/ResourceDisk.EnableSwap=y/g' /etc/waagent.conf
+#sed -i 's/ResourceDisk.SwapSizeMB=0/ResourceDisk.SwapSizeMB=4096/g' /etc/waagent.conf

Azureではシステムデーモンとしてwaagentというものがインストールされており元イメージにインストールされているようなのですが、今回はプレーンなLXDのイメージを利用しているためコメントアウトしています。

定期的なビルド

ご紹介したパッチを適用した上でLXDのイメージをビルドする作業を自動的に毎朝行っています。actions/virtual-environmentsは活発にアップデートが行われているため、すぐに変更に追従できるように定期的なチェックを行うためです。mainブランチを参照しているためたまにビルドに失敗しますが、高速に対応されるため少し待ってリトライすることで概ね成功します。

毎日ビルドを行っています

actions/virtual-environments はそれ自体が巨大なリポジトリであることとpackerの処理上並列に実行することが現状できないためビルドに非常に時間がかかります。Azure Pipelineのページから実行時間を確認することができますが、現時点ではLinux版が1番短く約2時間、もっとも長いWindows版では約5時間かかる計算になります。2 毎日行っているLXD向けビルドも2時間半ほどかかっています。

パッチを作成する際にはある程度試行錯誤していたのですが、あまりに長くて途方に暮れていたりしていました。

LXD向けビルドにGitHub Actionsを利用している大きな部分はオープンソースとして運用できる点と、起動しているGitHub-hosted runnerがUSに存在する点3です。

とくにactions/virtual-environmentsの実行は多くがインターネット経由でファイルのダウンロード処理を行っていることから、日本でコンピュートリソースが多い環境よりもGitHub-hosted runnerのようにある程度リソースに限りがあろうともUSに存在している方が高速にビルドすることができました。実際に日本で96コア/1TBメモリの環境でビルドを行うよりもGitHub-hosted runnerの2コア/7GBメモリのほうが若干高速にビルドすることができました。

また、GitHub-hosted runnerはディスク容量が14GB存在することが保障されています。しかしこれは「空き容量が14GB存在している」ということが保障されているということで、runner自体にはもう少し大きいディスクが接続されています。

LXD版のイメージを作る場合、「LXDデーモンが管理しているOS用の容量」と「成果物としてエクスポートするためのディスク容量」の2つが必要になります。

whywaita/virtual-environments-lxd の成果物は概ね17GB程度です。14GBの空き容量が確保されているものの17GB * 2の容量が必要になりそのままでは足りません。 その対策として packer build を行う前に実行に不要なファイルを削除しています。

https://github.com/whywaita/virtual-environments-lxd/blob/075f07913c48c22a80be532a1bbaa342c9da1703/.github/workflows/nightly_build_lxd_image.yaml#L19-L27

      - name: Delete unused file
        run: |
          sudo rm -rf /opt/hostedtoolcache
          sudo rm -rf /opt/ghc
          sudo rm -rf /usr/local/*
          # ignore /var/run, snap lxd packages
          sudo ls /var/**/** | grep ":" | tr -d ":" | grep -Ev "/var/run|/var/snap/lxd|/var/lib/snapd" |  xargs -I%% sudo rm -rf %%
          # ignore /usr/share/dpkg
          ls /usr/share/ | grep -v dpkg | xargs -I%% sudo rm -rf /usr/share/%%

これらのファイルの大部分は actions/virtual-environments でインストールされたものなので、virtual-environments をビルドするために virtual-environments のビルド成果物を消すという不思議な作業をしていました。通常のビルドであれば豊富なディスク容量を確保することをオススメします。

おわりに

actions/virtual-environments のセルフビルド方法についてご紹介しました。毎日ビルドすることによりフレッシュなイメージを常に利用することができますし、言語のアップデートにも耐えられるようになりました。

また「GitHub-hosted runnerと同じイメージを利用しています」というのは我々のサービスを利用する際にもユーザの躓きポイントを1つ消すことができたため効果が大きかったと感じました。我々のようにGitHub-hosted runner互換のサービスを開発している方の参考になれば幸いです。

冒頭に紹介したスライドにも書いてありましたが、現在はmyshoesを用いたサービス「Cycloud-hosted runner」を社内向けに提供しています。 2021/09/21にGeneral Availabilityと銘打ち、現在はグループ会社を含め多くのプロダクトに採用していただいています。

今回ビルドしたイメージを用いてサービス提供を行っていますが、快適に利用していただくためにいくつかの工夫を行っています。こちらの話に関してはまたの機会に。

myshoesやCycloud-hosted runnerも引き続きアップデートを行っていく予定ですので、もしプロダクト開発や新規研究などに興味のある方はぜひ私までご連絡ください。

現在募集中のCA Tech missionでは内製DBaaSや内製推論基盤の新規開発プロジェクトもありますので興味のある方はぜひ。

また、学生向けにデータセンター見学会 and 社員座談会の募集も始まっています。こちらも興味のある方はぜひご応募ください。

データセンター見学会と社員座談会募集

明日の担当はtoriimiyukkiさんです。誕生日が1日違いなのですが毎年その話をして毎年驚かれています。


  1. LinuxとWindowsのみ。macOSは以前は外部サービスを利用していましたが、現在はGitHubが管理するmacOS Cloudで管理しているようです↩︎
  2. 高速化のDiscussionも古くからありますがまだ道は見えていないようです https://github.com/actions/virtual-environments/discussions/2320↩︎
  3. 公式発表は見つけられず、IPアドレスで判定しています https://github.com/whywaita/ci-research/runs/4435490280?check_suite_focus=true↩︎