GitHub Actions: VirtualBoxを使って.deb/.rpmパッケージをテストする - 2022-07-25 - ククログ

ククログ

株式会社クリアコード > ククログ > GitHub Actions: VirtualBoxを使って.deb/.rpmパッケージをテストする

GitHub Actions: VirtualBoxを使って.deb/.rpmパッケージをテストする

最近GNU/Linux系のパッケージング周りの仕組みに初めてチャレンジしている福田です。

GitHub Actionsにおける.deb/.rpmパッケージのテストにVirtualBoxが便利だったので、今回はその方法についてご紹介します。

VirtualBoxとDockerのどちらをテストに使うか

GitHub Actionsで複数のディストリビューション向けのテストをする場合、Dockerが便利です。すぐに起動しますし、ビルド結果をキャッシュすることもできるので、CIの時間を短縮しやすいからです。 ただ、Dockerでは上手くテストできないケースがあります。

たとえば、サービスとして動く.deb/.rpmパッケージのケースです。Docker内ではsystemdが動いていないのでsystemdを使ってサービスを動かす.deb/.rpmパッケージをテストできません。 このようなケースではVirtualBoxが便利です。

DockerはOSを仮想化するだけでマシンを仮想化するわけではありません。つまり、マシンにインストールしたディストリビューションのような普通の起動処理は使われません。普通の起動処理とは、ざっくり言うと、まずブートローダーが起動して、カーネルを読み込んで、/sbin/initを起動するというような処理です。一方、VirtualBoxはマシンを仮想化するので普通の起動処理と同じ処理で起動した環境を使えます。 サービスとして動く.deb/.rpmパッケージは普通の起動処理で起動した環境用に作られているので、普通の起動処理で起動した環境でテストするべきです。ということで、サービスとして動く.deb/.rpmパッケージのテストにはDockerよりもVirtualBoxの方が向いています。

もう少し詳しい説明が欲しい場合は、たとえば、以下のDockerの公式サイトにあるコンテナーと一般的な仮想マシンとの違いの説明をご覧ください。

VagrantでVirtualBoxを使う

Vagrant1は、VirtualBoxなどの仮想化ツールを便利に扱える強力なツールです。

Vagrant自身は仮想化の機能を一切持ちませんが、VirtualBoxなどの仮想化ツールのフロントエンドとなり、 これを利用することで仮想環境を用いるワークフローの自動化などを簡単に行えます。

今回はこれを利用してテストを行います。

GitHub ActionsでVagrantを使う方法

それでは、GitHub ActionsでVagrant経由でVirtualBoxを使ってテストする方法を紹介します。

macOSを使ったテスト用のjobを設定する

GitHub Actionsではjobの環境をruns-onで設定しますが、Vagrantを使いたい場合はmacos-12を設定します。

これは、GitHub Actionsが提供しているUbuntuのrunnerはネストした仮想化機能が有効になっていないからです2。ネストした仮想化機能が有効になっていないとUbuntuのrunnerでVirtualBoxを使えません。

2022-07-22現在、macos-12のrunnerではネストした仮想化機能が有効になっています。また、デフォルトでVagrantもインストールされているのですぐにVagrant経由でVirtualBoxを使えます3

GitHub Actionsの大枠の設定は次のようになります。

# {...}
jobs:
  build:
    name: Build
    # {パッケージのビルド}

  test:
    name: Test
    needs: build
    runs-on: macos-12
    # {Vagrant環境でテスト}

needsを指定して、buildのjobが終わったら、testのjobが走るようにしています。

他のjobでビルドしたパッケージを取得する

テストのjobはビルドのjobと分かれているので、テストのjobの環境でビルドしたパッケージを取得する必要があります。 それには次のアップロードとダウンロードのアクションを使います。

ビルドのjobでパッケージをアップロードしておき、テストのjobでそれをダウンロードして利用します。

これを踏まえると、大枠の設定例は次のようになります。

{...}
jobs:
  build:
    name: Build
    {パッケージのビルド}
    - uses: actions/upload-artifact@v3
      with:
        name: {アップロードする名前}
        path: {アップロードするパス}
    {後処理}

  test:
    name: Test
    needs: build
    runs-on: macos-12
    steps:
      {前処理}
      - uses: actions/download-artifact@v3
        with:
          name: {アップロード時に指定した名前}
          path: {ダウンロード先のパス}
      {Vagrant環境でテスト}

Vagrantを使う

Vagrantを使うために、まずプロジェクトにVagrantfileを追加します。今回実際に利用したVagrantfileは次です。

# -*- mode: ruby -*-
# vi: set ft=ruby :

# Vagrantfile API/syntax version. Don't touch unless you know what you're doing!
VAGRANTFILE_API_VERSION = "2"

Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
  vms = [
    {
      :id => "centos-7",
      :box => "bento/centos-7",
    },
    {
      :id => "debian-bullseye",
      :box => "bento/debian-11",
    },
    {
      :id => "almalinux-8",
      :box => "bento/almalinux-8",
    },
    {
      :id => "almalinux-9",
      :box => "bento/almalinux-9",
    },
    {
      :id => "ubuntu-bionic",
      :box => "bento/ubuntu-18.04",
    },
    {
      :id => "ubuntu-focal",
      :box => "bento/ubuntu-20.04",
    },
  ]

  n_cpus = ENV["VM_N_CPUS"] || 2
  n_cpus = Integer(n_cpus) if n_cpus
  memory = ENV["VM_MEMORY"] || 1024
  memory = Integer(memory) if memory
  vms.each do |vm|
    id = vm[:id]
    box = vm[:box] || id
    config.vm.define(id) do |node|
      node.vm.box = box
      node.vm.provider("virtualbox") do |virtual_box|
        virtual_box.cpus = n_cpus if n_cpus
        virtual_box.memory = memory if memory
      end
    end
  end
end

複数のディストリビューションを管理しています。 前半のvms変数の内容を編集することで、用いるディストリビューションの種類を設定できます。

:idの値はconfig.vm.defineに用いられ、後述するvagrant upvagrant sshで指定するイメージの名前になります。

:boxの値はconfig.vm.box(上例ではnode.vm.box)に用いられ、これが利用したいディストリビューションになります。 例えば以下のサイトから利用したいディストリビューションを探して、その値を設定します。

以上のように:id:boxの2つの値を設定することで、複数のディストリビューションを扱うことができます。

詳しいVagrantfileの設定方法や仕組みについては、次のドキュメントをご覧ください。

Vagrantfileを追加したら、次のようにテストのjobのstepを設定します。

- name: Run VM
  run: |
    vagrant up {用いるイメージ名}
- name: Run test
  run: |
    vagrant \
      ssh {用いるイメージ名} \
      -- \
      {テスト実行ファイル}

「用いるイメージ名」には、Vagrantfileで設定した:idの値を指定します。

Vagrantでは、立ち上げたディレクトリが仮想環境内の/vagrantディレクトリにマウントされるので、ダウンロードしたパッケージは /vagrantからアクセスすることができます。

このようにすることで、ビルドしたパッケージを好きな環境でインストールし、起動できるかどうかなどを確認することができます。

例: RHEL系において、サービスとして正常に起動するかどうかをテストする

例えば以下のようにパッケージをインストールし、サービスとして起動できるかどうかをテストすることができます。

テストファイル: pacakge/test.sh

{事前準備}

sudo dnf install -y /vagrant/{パッケージのパス}

! sudo systemctl status {サービス名}
sudo systemctl enable --now {サービス名}
sudo systemctl status {サービス名}

GitHub Actionsのテストのjobのstep設定例

- name: Run VM
  run: |
    vagrant up almalinux-9
- name: Run test
  run: |
    vagrant \
      ssh almalinux-9 \
      -- \
      /vagrant/pacakge/test.sh

まとめ

本記事では、GitHub ActionsとVirtualBoxを使って.deb/.rpmパッケージをテストする方法について紹介しました。

実際に設定したGitHub Actionsは、次になります。

ここではstrategymatrixを用いることで、複数の環境向けのパッケージのビルドとテストを管理しています。

クリアコードではこのように業務の成果を公開することを重視しています。業務の成果を公開する職場で働きたい人はクリアコードの採用情報をぜひご覧ください。

  1. https://www.vagrantup.com/intro

  2. GitHub Actionsが提供しているUbuntuのrunnerは仮想マシン上で動いているので、仮想マシン上でさらに仮想マシンを動かすにはネストした仮想化機能を有効にする必要があります。しかし、Ubuntuのrunnerを動かしているAzureのインスタンスの種類ではネストした仮想化機能を有効にできません。そのためGitHub Actionsが提供しているUbuntuのrunnerでもネストした仮想化機能を有効にできません。参考:調べたユーザーのコメント中の人からのサポートしていないという回答

  3. macos-11はデフォルトでVagrantを使えないので、注意して下さい。