vagrant ssh とスクリプトを使ってゲストOS(Ubuntu)の環境構築を自動化してみる

本記事では、VirtualBox + Vagrant + Ubuntu のベース BOX から仮想マシンを作成した後に、Windows のスクリプトと、vagrant ssh、Ubuntu のスクリプトを組み合わせて環境構築を自動化する簡単な方法をメモしておきます。

【目次】

[1]はじめに

VirtualBox + Vagrant + Ubuntu のベース BOX を利用すると、簡単に Ubuntu の仮想マシン環境を用意することができます。

ところで、用意した仮想マシン環境を開発やテストに利用するためには、基本的なOSレベルの環境に加えて、プログラミングツールやミドルウェア、データベース環境の構築など、定型的だけど面倒な作業が必要です。

Vagrant は、元のベース BOX をカスタマイズすることができますので、これらのツール群をインストールした BOX を作成することもできます。

しかし OS は比較的長いスパンで安定して使える一方、開発ツール群(特に開発言語やライブラリなど)は OS よりもバージョンアップのサイクルが早いように思えますし、プロジェクト毎に必要となるツール群のバージョンが異なる場合もあります。

ツール群のバリエーションやテストデータも含めた BOX のバリエーションを増やしてしまうと、何が何だか分からなくなるのに加えて、個人的にはディスクを浪費するのが辛いです。

その一方、アプリ開発環境については、ソースコードの準備から必要なライブラリをインストールして実行するステップは、プログラミング言語毎に優秀なパッケージマネージャがありますので、かなり自動化できます。
(リポジトリからソースコードを取得して、npm、pip、 maven などのインストールコマンドを実行するパターンです。)

つまり、基本的な OS レベルの環境と、具体的なプログラム開発環境は手軽にセットアップできる状況ですが、その間を埋めるための手順である開発ツール群の環境構築のところが手軽にできないなー、と感じており、これらもある程度自動化したいところです。

こういった用途のために Vagrant には最初の起動時のみ実行してくれる provision の機能があるのではないか?と思われますが、インストール後に再起動が必要になるツールには使いづらいし、途中でスナップショットを取ることもできません。

かといって開発ツール群やデータベース環境の構築は、バリエーションは多いものの、実際にやることはコマンドを連続して実行する程度だったりしますので、(手軽に環境を作ることを目的とする場合は)環境構築ツールを導入するほどでもない気もします。

そこで本記事では、VirtualBox + Vagrant + Ubuntu のベース BOX から仮想マシンを作成した後に、ホストOS側(本記事ではWindows)のスクリプトと、vagrant ssh とゲストOS側(本記事では Ubuntu)のスクリプトを組み合わせて実行することで、ツール群などの構築を自動化する簡単な方法をメモしておきます。

これは極めてローテクで当たり前の内容ですが(笑)、私はとても重宝してます。

この方法だと、仮想マシンの再起動が必要になるインストールもある程度は制御できますし、構築途中の段階でスナップショットを取ることもできます。

何より、仕組みが素朴なので、手軽に始められて、環境構築するタイミング毎に少しづつ手を入れてスクリプトを強化すればよいという、ストレスフリーなところがいいです(笑)。

(参考記事)

[2]スクリプトの構成

(1)基本的な考え方

まず、動作を理解するのに必要な知識を簡単に整理しておきます。
  • vagrant up
    • VirtualBox の仮想マシン(ゲストOS)を起動するコマンド
  • vagrant halt
    • VirtualBox の仮想マシン(ゲストOS)を停止するコマンド
  • vagrant reload
    • VirtualBox の仮想マシン(ゲストOS)を再起動するコマンド
  • vagrant ssh [仮想マシンで実行するコマンド]
    • vagrant ssh は仮想マシンに vagrant ユーザでログインする。
    • [仮想マシンで実行するコマンド]を省略すると、対話型シェルを利用できます。
    • [仮想マシンで実行するコマンド]に Ubuntu 環境で実行したいコマンドを指定すると、vagrant ユーザでログイン後に指定のコマンドを実行して終了(ログアウト)します。
      • Ubuntu 内で指定のシェルスクリプトを実行できます。
  • Windows側の Vagrantfile があるディレクトリは、Ubuntu 側で /vagrant にマウントされています。
    • 例えば、Vagrantfile があるディレクトリに setupkit というディレクトリがあり、その下に、setup.sh というファイルがあれば、仮想マシン側(Ubuntu)からは、「/vagrant/setupkit/setup.sh」でアクセスできます。

(参考)Vagrant のコマンドなどについては以下の記事も参考にしてください。

ここでのポイントは、Windows 側の Vagrantfile があるディレクトリ以下に Ubuntu 内で実行したいシェルスクリプトを用意しておけば、vagrant ssh を使って Ubuntu 内で実行できるので、環境構築用のスクリプトを実行できるということです。

そして、Ubuntu 内で実行するシェルスクリプトからは Vagrantfile があるディレクトリを /vagrant として参照できますので、ここに環境構築に必要なデータを用意しておけばシェルスクリプト内で利用できます。

さらに、vagrant XXX のようなコマンドは Windows 側で実行するコマンドですので、仮想マシンの環境構築手順を Windows のバッチファイル(または PowerShell のスクリプト)で制御できることになります。

そこで、Windows 側に Ubuntu 環境の構築用のシェルスクリプトやデータを用意して、これらを制御する Windows 側のバッチファイル(または PowerShell のスクリプト)から実行することで、環境構築の自動化を行います。

なお、以下では Windows 側のスクリプトにはバッチファイルを利用します。これは PowerShell に読み替えていただいても問題ありません。([4]を参考にして下さい。)

(2)ディレクトリ構成例

考え方は単純ですが、何らかの規則があったほうが考えやすいので、私の構成方法をメモしておきます。

後述の実例を見れば一目瞭然だとは思いつつ、まずは基本的な考え方をメモしておきます。

Vagrantのプロジェクトディレクトリ
├─ Vagrantfile
└─ setupkit\  :セットアップキットのディレクトリ
  ├─ setup.bat  : 自動構築バッチファイル
  ├─ componentA\  : コンポーネントA用ディレクトリ
  │ ├─ setup.sh :  インストールスクリプト
  │ └─ data\  : インストールするデータなどのディレクトリ
  ┋
  └─  componentN\  : コンポーネントN用ディレクトリ
    ├─ setup.sh :  インストールスクリプト
    └─ data\  : インストールするデータなどのディレクトリ

  • Vagrantfile があるディレクトリに、「setupkit」というディレクトリを作ります。
    • ディレクトリ名は何でもよいのですが、Ubuntu のシェルスクリプトから参照する場合を考えて、名前を固定した方が良いと思います。
  • 「setupkit」の下にバッチファイル「setup.bat」を置きます。
    • 「setup.bat」は、vagrant コマンドを利用して仮想マシンの状態制御と、各コンポーネントのセットアップシェルスクリプト(setup.sh)を実行して、環境構築を自動化します。
    • PowerShell のスクリプト(例えば「setup.ps1」)に置き換えても問題ありません。
  • 「setupkit」の下に Ubuntu にセットアップするコンポーネントごとのディレクトリを作ります。
    • コンポーネントのディレクトリの下に、Ubuntuで実行するシェルスクリプト「setup.sh」を作ります。これはバッチファイル(setup.bat)から呼び出されます。
    • セットアップに必要なデータがあるときは、コンポーネントのディレクトリの直下に配置するか、必要に応じてディレクトリなどを作ってデータを用意し、シェルスクリプトから利用します。
      • tar や deb で配布されているキットなどを置いておけば、Ubuntu 側からアクセスできますので、setup.sh から利用できます。
      • テストデータなども同様に setup.sh から利用できますので、初期データの設定に利用できると思います。

(3)実行方法

setupkit にあるバッチファイル「setup.bat」をダブルクリックして実行します。

すると(まだ作成されていない場合は) Vagrantfile にもとづいて仮想マシンが作成され、コンポーネントのセットアップを行って終了します。

大まかな動作は以下の通りです。
  • バッチファイルを実行したらコマンドプロンプトが開きます。
  • バッチファイル内で最初に vagrant up コマンドが実行されるとき、setupkit ディレクトリの上にある Vagrantfile を自動的に検出して、BOXイメージから仮想マシンの作成を行ってくれます。
  • バッチファイルに書かれているコンポーネントセットアップ用シェルスクリプトの実行や仮想マシンの再起動、スナップショット作成などを実行します。
  • バッチファイルの実行が終了するとコマンドプロンプトが閉じて環境構築が終了します。

なお、Vagrantfile の設定が「vb.gui = true」となっている場合はバッチファイルの実行中に仮想マシンの画面が表示されますが、バッチファイルが終了するまで放置しておきます。

(4)運用例:使い回し

私の場合は setupkit を毎回作ることはせず、使い回し(状況に応じて修正)です。

直近で利用した setupkit をコピーして、目的の環境に合うように不要なものはリネームしたり(基本的に削除はしません)、必要なものは新たに追加する運用をしています。

もともと、仕事のプロジェクト用に厳密に作り込むことを目的としておらず、新しいライブラリや技術を試すために、普段使っている環境とは別の環境を簡単に作ることを目的としているため、setupkit は管理といえるものは殆どしません。

また、スクリプトは条件分岐などの複雑な制御?は極力使用せず、目的の環境に合わせてコマンドを並べるだけにします。(後で見たとき実行内容が一目瞭然だし。)

あまり真剣に考えて高度化しようとすると、セットアップツールの制作レベルになってしまって逆にストレスが溜まりますので、私の場合、極力ローテクっぽく「ほどほど」に運用することでストレスフリーにしています。

[3]簡単なWEBコンテンツのテスト環境サンプル

ショボい例で大変恐縮ですが、本記事では
  • Docker 上で動作する NGINX をWEBサーバとして利用する HTML コンテンツのテストや修正を行うための簡単な環境
を構築してみます。

ただ、これだけだと Vagrant を使わなくても Docker だけでよいのでは?となりますので、ここでは追加で以下のことができるようにします。
  • ホストOS環境に影響されないテスト環境として、Ubuntu 日本語デスクトップ環境内でブラウザを利用してテストができる。
VirtualBoxのネットワーク設定を行えば、ホストOSから Ubuntu のWEBサーバにもアクセスできる。

(1)setupkit の例

VirtualBox / Vagrant のプロジェクト名を「test_contents」として、以下のディレクトリ構成で作成してみます。

test_contents:コンテンツのテスト環境用プロジェクトディレクトリ
├─ Vagrantfile
└─ setupkit\  :セットアップキットのディレクトリ
  ├─ setup.bat :自動構築バッチファイル
  ├─ docker\  : Docker セットアップ用ディレクトリ
  │ └─ setup.sh :  Docker インストールスクリプト
  └─ test_contents\  : コンテンツセットアップ用ディレクトリ
    ├─setup.sh : NGINXとテストコンテンツのインストールスクリプト
    └─ contents\  : コンテンツのディレクトリ
      └─ index.html

以下にファイルの例を見ていきます。

①ディレクトリの作成

mkdir  test_contents
cd  test_contents

②Vagrantfile の例

Vagrant.configure("2") do |config|
  config.vm.box = config.vm.box = "ubuntu20_jpgui"

  config.vm.provider "virtualbox" do |vb|
    vb.gui = true
    vb.memory = "8192"
    vb.cpus = 2
    vb.customize [
      "modifyvm", :id,
      "--vram", "256",
      "--graphicscontroller", "vmsvga",
      "--accelerate3d", "off",
      "--vrde",  "off",
      "--clipboard", "bidirectional",
    ]
  end
end

test_contents ディレクトリに Vagrantfile を作成します。

上記の例では Ubuntu 20.04 LTS 日本語環境のベースBOXとして、記事『Ubuntu 20.04 LTS 日本語デスクトップ環境のVagrant BOXを作る』で作成したBOX(ubuntu20_jpgui)を使いますが、これに限るものではありませんので、設定内容を含めて適当に作成してください。

③setup.bat の例

@rem Ubuntu 起動(仮想マシンの作成)
vagrant up

@rem Docker インストール
vagrant ssh -c ". /vagrant/setupkit/docker/setup.sh"

@rem Ubuntu 停止
vagrant halt

@rem スナップショット作成
vagrant snapshot save docker_setup

@rem Ubuntu 起動
vagrant up

@rem NGINXとテストコンテンツのインストール
vagrant ssh -c ". /vagrant/setupkit/test_contents/setup.sh"

@rem Ubuntu 停止
vagrant halt

@rem スナップショット作成
vagrant snapshot save test_contents_setup

vagrant ssh を利用して setupkit ディレクトリ下の docker ディレクトリと test_contents ディレクトリの下にある setup.sh をそれぞれ呼び出しています。

この内容程度なら、途中の再起動やスナップショットを2回とる必要はないと思いますが、サンプルとして手順を入れています。

④docker\setup.sh の例

# Docker Engine をインストール
# (参考) https://docs.docker.com/engine/install/ubuntu/

sudo apt-get update

sudo apt-get instal -yl \
    ca-certificates \
    curl \
    gnupg \
    lsb-release

sudo mkdir -p /etc/apt/keyrings

curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg

echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
  $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

sudo apt-get update
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin

ここでは、Docker Desktop ではなく、Docker Engine でインストールしてみます。

上記スクリプトの内容は以下のドキュメントの手順に「-y」オプションを追加しただけです。

⑤test_contents/setup.sh の例

# setupkitのcontentsを仮想マシンにコピー
cp -r /vagrant/setupkit/test_contents/contents/  /home/vagrant/contents/

# contents ディレクトリをバインドしてNGINXをセットアップ
sudo docker run --name webserver  --mount type=bind,source=/home/vagrant/contents,target=/usr/share/nginx/html,readonly -p 80:80 -d --restart=unless-stopped nginx

Windows 側に用意しているテスト用コンテンツのディレクトリ(test_contents\contents)の内容を Ubuntu 側のホームディレクトリ下の contents ディレクトリにコピーし、NGINX がコピーした内容を見るようにしています。

また --restart オプションを利用してポート 80で常時 起動にしています。

⑥test_contents/index.html の例

<!doctype html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>WEBコンテンツのテスト</title>
</head>
<body>
  <h1>Hello World!</h1>
  <p>コンテンツのテスト中です</p>
</body>
</html>

サンプルのためだけのサンプルです(笑)。

(2)動作確認

setupkit ディレクトリにある「setup.bat」をダブルクリックして実行します。(バッチファイルが終了するまで待ちます。)

これにより、test_contents ディレクトリにある Vagrantfile の内容にもとづいて仮想マシンが作成されて環境構築とスナップショットを取って終了します。

バッチファイルがちゃんと終了したようであれば、実際に構築した環境を使ってみます。

「vagrant up」で仮想マシンを起動して、Ubuntuにログインします。

Firefoxを開いて、「http://localhost」にアクセスすると以下のように表示されると思います。

コンテンツ用環境を自動構築した画面

また、Ubuntuのホームディレクトリ下の contentsディレクトリにある index.html を適当に変更してブラウザを更新すると、変更内容が反映されると思います。(setupkit内の元ファイルは変更されません。)

もし期待通り動作しない場合は「vagrant destroy」で仮想マシンを破棄して、スクリプトを修正後、再度「setup.bat」を実行します。([4]参考情報も参考にしてください。)

このときスナップショットを利用すれば、スナップショットを取った状態を復元できますので、どの段階で問題があるかを特定するヒントになります。

スナップショットの状況は以下のコマンドで確認できます。

vagrant snapshot list

VirtualBox と Vagrant のスナップショットについては以下の記事も参考にしてください。

最後に、このサンプルを残しておくとディスクの肥やしになるだけですので(笑)、「vagrant destroy」で仮想マシンを破棄します。

[4]参考情報

(1)Windowsで Ubuntu のシェルスクリプトを作成する場合の注意事項

Ubuntu で実行するシェルスクリプトを Windows 環境で作成する場合は、文字エンコードを UTF-8、改行を LF にする必要があります。
日本語を含んでなければ文字エンコードは問題ないと思いますが、改行コードが CRLF になっている場合、スクリプトを実行すると以下のようなエラーになります。

bash: $'\r': コマンドが見つかりません

なお、メモ帳でスクリプト内容を確認する場合、メモ帳のステータスバーに文字エンコードが表示されていますが、こちらは保存時に指定することができます。

一方、改行コードもステータスバーに「Unix (LF)」とか「 Windows (CRLF) 」と表示されていますが、これを編集中に変更する方法は無いような気がします。
(ネットで検索するとレジストリで設定は変更できるようですが…)

(2)PowerShell のスクリプト

正直なところ、私は複雑な処理にはプログラムを作ってしまうタイプの人間なので、 Windows のバッチファイルはコマンドの実行くらいしか利用する機会が無く、ほぼ素人です。当然 PowerShell についても素人です。

そんな私ですが、バッチファイルの「.bat」を「.ps1」に変更したら PowerShell で動くのかな?と思ったら動かなかったので(涙)、バッチファイルと PowerShell のスクリプトについて、超初心者レベルで(自分用に)メモしておきます。
(PowerShell に詳しい方は以下の内容はスルーしてください。)

  • Windowsの既定状態では PowerShell のスクリプトは実行できません。
  • Windows のバッチファイルはダブルクリックで実行できますが、PowerShell のスクリプトは実行できないようです。
    • ダブルクリックするとメモ帳が開いてソースコードが表示されます。右クリックのメニューから「PowerShellで実行」を選択して実行できます。
  • バッチファイルはコメントを「rem」で始めますが、PowerShellのスクリプトは「#」です。
    • 本記事の例だと「@rem」を「#」に置き換え、拡張子を「ps1」にし、PowerShellを実行できる状態にすると、同じように動作しました。
  • PowerShellは高機能であり、バッチファイルではできないことが実現できます。
    • 但し、本記事の Windows 側の処理内容程度だとオーバースペックという気がしますが、ダブルクリックで実行できる手軽さから私はバッチファイルを利用しています。
    • ちなみに、Ubuntu の環境構築に高度な制御が必要な場面は、Windows側の制御よりも Ubuntu 側のスクリプト側になりますが、bash スクリプトは十分高機能だと思います。

コメント

このブログの人気の投稿

VirtualBoxのスナップショット機能

Google Document AIで画像から表形式データを抽出する(Vision API OCRとの違い)

Ubuntu/Colab環境でPDFファイルのページを画像化する(pdf2image、pdftoppm、pdftocairo)