CLOVER🍀

That was when it all began.

vagrant-libvirtを使って仮想マシンを起動した時に、NFSが使えない場合にエラーになるのを回避する

これは、なにをしたくて書いたもの?

Vagrant仮想マシンのプロバイダーにlibvirtを使っている時、NFSに関するエラーが出て仮想マシンが起動しなかったという事象に遭遇し、
さてどうしましょうと調べてみた話。

その内容を書こうかな、と。

環境

Ubuntu Linux 18.04 LTSで、Vagrantは2.2.5。

$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 18.04.3 LTS
Release:    18.04
Codename:   bionic


$ vagrant -v
Vagrant 2.2.5

Vagrantを使える環境は2つあって、ひとつは仮想マシンのプロバイダーがVirtualBox

$ VBoxManage -v
6.0.12r133076

もうひとつはlibvirt

$ libvirtd -V
libvirtd (libvirt) 4.0.0

$ kvm --version
QEMU emulator version 2.11.1(Debian 1:2.11+dfsg-1ubuntu7.19)
Copyright (c) 2003-2017 Fabrice Bellard and the QEMU Project developers

Debian仮想マシンを起動する

ちょっとDebian仮想マシンに用があって、以下のBox(プロバイダーとしては、VirtualBoxlibvirt用のものが存在)を使って

Vagrant box debian/buster64 - Vagrant Cloud

$ vagrant init debian/buster64

起動。

$ vagrant up

### プロバイダーがlibvirtの場合は
$ vagrant up --provider=libvirt

VirtualBoxの方はうまくいくのですが、libvirtの方はNFSが使えないと言われてエラーになり、仮想マシンが起動しません。

It appears your machine doesn't support NFS, or there is not an
adapter to enable NFS on this machine for Vagrant. Please verify
that `nfsd` is installed on your machine, and try again. If you're
on Windows, NFS isn't supported. If the problem persists, please
contact Vagrant support.

なにがエラーになっているかというと、Synced Folderです。ホスト側でNFSが使えないのが原因です。

DebianのBoxのVagrantfileを見ると、確かにSynced FolderにNFSを使うようになっています。

  config.vm.synced_folder ".", "/vagrant", type: "nfs", nfs_version: "4", nfs_udp: false

Synced Folderの設定を変える

Synced Folderの実現方法には、いろいろあるみたいですね。

Synced Folders - Vagrant by HashiCorp

エラーメッセージを調べると、みんな「NFSをインストールしよう」となっているのですが…そりゃあそうですよね。

Vagrant: synced_folder を nfs マウントする | deadwood

linux - When “vagrant up” it says “It appears your machine doesn't support NFS” (Debian jessie) - Stack Overflow

でも、このためだけにわざわざNFSサーバーを導入するのは、それはそれで嫌だなぁと…。

で、vagrant-libvirtプラグインのSynced Folderの設定に、なにか載ってたなぁと思って調べてみます。

Synced Folders

以下のどちらかを設定すると、とりあえず起動するようになります。

  config.vm.synced_folder './', '/vagrant', type: '9p', disabled: false, accessmode: "squash", owner: "1000"

# または

  config.vm.synced_folder './', '/vagrant', type: '9p', disabled: false, accessmode: "mapped", mount: false

後者だと、マウントがなくなりますが。

accessmodeの意味は、libvirtの設定になるので、こちらを参照。

libvirt Domain XML Format / Filesystems

Starting the Guest using libvirt

9pについては、こちら。

9P - Wikipedia

Virtfs (Plan 9) vs NFS as tool for share folder for virtual machine - Unix & Linux Stack Exchange

たまに、以下の表示で止まりますが、再度仮想マシンを起動し直すと良いかも…。

==> default: Waiting for domain to get an IP address...

他のBox(vagrant-libvirt)を使った時の起動ログを見ていると、Rsyncで実現したりしているものもあります。

==> default: Installing rsync to the VM...
==> default: Rsyncing folder: /path/to/ => /vagrant

Synced Folderについては、VirtualBoxをプロバイダーにした方がお手軽かもしれませんね。
※パフォーマンスはNFSの方が有利らしいですが

Vagrant + VirtualBox で nfs を使って、synced_folder を速くする - Shin x blog

Vagrantのsynced_folderのtypeを比べてみた | 東北ギーク

もしくは、割り切ってネットワークの設定でIPアドレスを固定してアクセスしたりしましょう。

Ubuntu Linux 18.04 LTSにApache DrillをEmbedded Modeでインストールして、CSVファイルを読んで遊ぶ

Apache Drill?

Apache Drillとは、さまざまなデータソースに対してクエリを実行できるソフトウェアです。

Apache Drill - Schema-free SQL for Hadoop, NoSQL and Cloud Storage

Apache Drill を利用した実データの分析

GoogleのDremelにインスパイアされて、開発されたということらしいです。

Dremel: Interactive Analysis of Web-Scale Datasets – Google AI

どういうデータソースにアクセスできるかというと、

Connect a Data Source Introduction - Apache Drill

f:id:Kazuhira:20191012215550p:plain

と、いったように、以下のようなデータソースが対象です。

また、ファイルを読む場合、対応するファイルフォーマットとしてはこちら。

Data Sources and File Formats Introduction - Apache Drill

f:id:Kazuhira:20191012210018p:plain

今回、このApache Drillを使って、ローカルファイルシステム上にあるCSVファイルに対してクエリを実行してみましょう。

環境

今回の環境は、こちら。

$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 18.04.3 LTS
Release:    18.04
Codename:   bionic


$ java -version
openjdk version "1.8.0_222"
OpenJDK Runtime Environment (build 1.8.0_222-8u222-b10-1ubuntu1~18.04.1-b10)
OpenJDK 64-Bit Server VM (build 25.222-b10, mixed mode)

Apache Drillの実行には、Oracle JDK 8またはOpenJDK 8が必要です。

以下を読み進めながら、インストール。

Apache Drillには2つの実行モードがあり、スタンドアロンで動かすのをEmbedded Mode、分散環境上で動作させるのを
Distributed Modeと呼びます。

今回は、スタンドアロンで動かすのでEmbedded Modeでインストールします。

Install Drill Introduction - Apache Drill

Embedded Mode Prerequisites - Apache Drill

Installing Drill on Linux and Mac OS X - Apache Drill

といっても、tar.gzファイルをダウンロードして解凍すればおしまいです。

$ wget https://www-us.apache.org/dist/drill/drill-1.16.0/apache-drill-1.16.0.tar.gz
$ tar xf apache-drill-1.16.0.tar.gz
$ cd apache-drill-1.16.0

起動。

Starting Drill on Linux and Mac OS X - Apache Drill

$ bin/drill-embedded
Apache Drill 1.16.0
"Think different, think Drill."
apache drill> 

そのまま、インタラクティブシェルが起動し、クエリを実行することができます。

また、この時にWeb UIも起動しています。

Starting the Web UI - Apache Drill

http://localhost:8047/」にアクセスしてみると、Web UIへアクセスできます。今回は、このWeb UIを中心に使っていきましょう。

f:id:Kazuhira:20191012205002p:plain

CSVファイルにクエリを投げてみる

プレーンテキストにクエリを投げるためのドキュメントは、こちら。

Querying Plain Text Files - Apache Drill

CSVに対してクエリを実行するサンプルがあるので、実行してみましょう。こんなファイルを用意。

plays.csv

1599,As You Like It
1601,Twelfth Night
1594,Comedy of Errors
1595,Romeo and Juliet
1596,The Merchant of Venice
1610,The Tempest
1599,Hamlet

「Query」を選んで、以下のクエリを実行してみます。

select * from dfs.`/path/to/plays.csv`

f:id:Kazuhira:20191012214714p:plain

結果は、こんな感じ。

f:id:Kazuhira:20191012214742p:plain

「Query Profile」を押すと、クエリの実行計画を見たり、クエリを修正して再度実行させたりできます。

f:id:Kazuhira:20191012214910p:plain

ただ、「Query Profile」を押すと、別タブが開くんですよね…。それにクエリを間違ったりすると修正できなくなったりするので、
UIとしてはもうちょっと良いものが欲しいかもですね。

同じクエリを、シェルで実行した場合はこうなります。

apache drill> select * from dfs.`/path/to/plays.csv`;
+-----------------------------------+
|              columns              |
+-----------------------------------+
| ["1599","As You Like It"]         |
| ["1601","Twelfth Night"]          |
| ["1594","Comedy of Errors"]       |
| ["1595","Romeo and Juliet"]       |
| ["1596","The Merchant of Venice"] |
| ["1610","The Tempest"]            |
| ["1599","Hamlet"]                 |
+-----------------------------------+
7 rows selected (0.112 seconds)

ところで、クエリを実行した時にファイルパスの前に書いていた

select * from dfs.`/path/to/plays.csv`

この「dfs」というのはなんでしょう。

Web UIから、「Storage」というものを選んでみます。

f:id:Kazuhira:20191012220202p:plain

ここに載っている「dfs」というやつですね。

これは、File System Storageです。

File System Storage Plugin - Apache Drill

設定を見ると、ローカルファイルシステムを対象にしています。from句に書いたファイルパスは、「workspace」と呼ばれるものからの
相対パスで指定するようです。今回は「/」がワークスペースに入っていますが。

{
  "type": "file",
  "connection": "file:///",
  "config": null,
  "workspaces": {
    "tmp": {
      "location": "/tmp",
      "writable": true,
      "defaultInputFormat": null,
      "allowAccessOutsideWorkspace": false
    },
    "root": {
      "location": "/",
      "writable": false,
      "defaultInputFormat": null,
      "allowAccessOutsideWorkspace": false
    }
  },

f:id:Kazuhira:20191012220136p:plain

ここで、File System Storageの設定を変えてみることを考えてみます。

例えば、先ほどのクエリの実行結果は、列が「columns」というようにまとめられていましたが、以下のように「as」を使うことで
列に別名を与えることができます。まあ、通常のSQLの話ですね。

select columns[0] as c1, columns[1] as c2 from dfs.`/path/to/plays.csv`

これで、列に別名を与えて実行できます。

f:id:Kazuhira:20191012221249p:plain

このようにクエリで列名を与えるのではなく、CSVファイルの中にヘッダーとして列名を書いていきたいと思います。

plays.csv

c1,c2
1599,As You Like It
1601,Twelfth Night
1594,Comedy of Errors
1595,Romeo and Juliet
1596,The Merchant of Venice
1610,The Tempest
1599,Hamlet

列名は、c1、c2としました。

このまま先ほどのクエリを実行すると、追加した列名は単なる値として扱われます。

f:id:Kazuhira:20191012224941p:plain

ここで、CSVファイルにヘッダーがあることを指定します。これは、File System Storageプラグインの設定です。

    "csv": {
      "type": "text",
      "extensions": [
        "csv"
      ],
      "delimiter": ",",
      "extractHeader": true
    },

dfsファイルストレージの設定を、これで保存。

設定項目は、こちらに書かれています。

Text Files: CSV, TSV, PSV - Apache Drill

ところで、File System Storageプラグインの設定を見ると、拡張子「csvh」であればヘッダー行ありのCSVとして認識してくれるようです。
ケースによっては、こちらを使っても良いのかもしれません。

    "csvh": {
      "type": "text",
      "extensions": [
        "csvh"
      ],
      "extractHeader": true,
      "delimiter": ","
    },

Apache Drillでは、ファイルの拡張子とフォーマットが紐づくようですね。

クエリの実行結果。

f:id:Kazuhira:20191012225107p:plain

ところで、デリミタなどの指定は可能なのですが、読み込むファイルのエンコーディングの指定はありません。
UTF-8でファイルが書かれていることが、前提になっているみたいですね。

2つのファイルをJOINしてみる

最後に、2つのCSVファイルをJOINしてみましょう。

以下のような2つのファイルを用意。
books.csv

isbn,title,price,category_id
978-4297100339,Docker/Kubernetes 実践コンテナ開発入門,3718,1
978-4798134888,詳解UNIXプログラミング 第3版,9130,2
978-4798053776,アプリケーションエンジニアのためのApache Spark入門,3740,3
978-4798152370,Apache Kafka 分散メッセージングシステムの構築と活用,3960,3
978-4774196077,Linuxのしくみ ~実験と図解で学ぶOSとハードウェアの基礎知識,3278,2

categories.csv

id,name
1,Docker
2,Linux
3,ビッグデータ

この2つのファイルを、JOINしてみます。

select book.isbn, book.title, book.price, category.name as category_name
from dfs.`/path/to/books.csv` as book
inner join dfs.`/path/to/categories.csv` as category
on book.category_id = category.id
order by book.price asc

結果。

f:id:Kazuhira:20191013011600p:plain

OKそうです。

まとめ

今回は、Apache DrillをEmbedded Modeでインストールして、CSVファイルを読み、クエリを実行するところまでを試してみました。

他にもJSONファイルが読めたり、RDBMSAmazon S3をデータソースにしたりといろいろできるのですが、それまたの機会に。