メモのページ - チラシの裏メモ 3枚目

通信技術や気になった事を黙々とメモし続ける

MirageOS Unikernelを、もう少しかじってみた

前回はMirageOS Unikernelという特定のプログラムを動作させるだけの極小OSを動かしてHello Worldを出力させて遊んでみた。
MirageOS含め、Unikernelのアーキテクチャの全貌を掴めたという訳ではないが、MirageOSへの興味が強くなった為、更に先に進んでみる事にした。
今回は、bridgeインターフェースとtapインターフェースを介し、サンプルプログラムを使ってMirageOSへ通信させてみた時のメモ。
※前回:https://debslink.hatenadiary.jp/entry/20200516/1589616362

2020年8月1日追記
Ubuntu 20.04 + MirageOS 3.8.0環境で再試行した結果、想定どおりの動作を確認。
当記事は再試行版に再編集した。


当方の環境
ホストOS:Windows7 32bit版 / RAM: 4GB / CPU: Intel Core i3-2350M 2.30GHz
ゲストOS:Ubuntu Server 20.04 x86 64bit版
UnkernelなOS:Mirage OS 3.8.0
Virtualbox 5.2.32
Ubuntu Serverは既にデプロイ済み
PCのCPU仮想化支援機構はBIOSにて有効済み

MirageOSのインストール手順は、当記事下部に載せたサイトを参考に進めた。
ただし、自分の環境ではhvtオプション下での通信テストが正常に実行出来なかった為、Virtioオプションでコンパイルし通信のサンプルプログラムを生成、実行した。

当方の環境で動作するLinuxカーネルとUbuntuのバージョンは以下のとおり。

$ uname -a
Linux hostname 5.4.0-42-generic #46-Ubuntu SMP Fri Jul 10 00:24:02 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux
$
$ cat /etc/lsb-release
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=20.04
DISTRIB_CODENAME=focal
DISTRIB_DESCRIPTION="Ubuntu 20.04.1 LTS"
$


当方の環境で動作するMirageOS Unikernelとopamのバージョンは以下のとおり。

$ mirage --version
v3.8.0
$ 
$  
#  switch   compiler                    description
→  4.10.0   ocaml-base-compiler.4.10.0  4.10.0
   default  ocaml-system.4.08.1         default

[WARNING] The environment is not in sync with the current switch.
          You should run: eval $(opam env)
$



インターフェースmirage_brとtap0の作成
MirageOSは通信時にtapインターフェースを使用する。
ホスト機内部でOS(Ubuntu)~MirageOS間で通信が出来るよう、bridgeインターフェース(当環境ではmirage_br)とtapインターフェース(当環境ではtap0)を作成。
ただし、以下は恒久的な設定ではなく、Ubuntuの再起動でbridgeインターフェースとtapインターフェースは消える為、恒久的に残したい場合は別途設定が必要。

$ sudo brctl addbr mirage_br
$ sudo ip link set mirage_br up
$ sudo ip tuntap add tap0 mode tap
$ sudo ip link set dev tap0 up
$ sudo brctl addif mirage_br tap0
$ 
$ sudo ip addr add 192.168.122.1/24 dev mirage_br
$


作成後、インターフェースの状態を確認。
作成直後はmirage_br、tap0共にDOWNのステータス。

$ ip addr show
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
2: enp0s17: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 08:00:27:40:3b:77 brd ff:ff:ff:ff:ff:ff
    inet 192.168.3.99/24 brd 192.168.3.255 scope global enp0s17
       valid_lft forever preferred_lft forever
    inet6 2400:2410:d441:a900:a00:27ff:fe40:3b77/64 scope global dynamic mngtmpaddr noprefixroute
       valid_lft 86258sec preferred_lft 14258sec
    inet6 fe80::a00:27ff:fe40:3b77/64 scope link
       valid_lft forever preferred_lft forever
3: mirage_br: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default qlen 1000
    link/ether 9e:77:cd:a6:c4:b2 brd ff:ff:ff:ff:ff:ff
    inet 192.168.122.1/24 scope global mirage_br
       valid_lft forever preferred_lft forever
    inet6 fe80::1cf3:e1ff:febf:3d54/64 scope link
       valid_lft forever preferred_lft forever
4: tap0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc fq_codel master mirage_br state DOWN group 

default qlen 1000
    link/ether 9e:77:cd:a6:c4:b2 brd ff:ff:ff:ff:ff:ff
$


mirage-skeletonディレクトリ配下のnetworkに移動。
config.mlファイル(構成ファイル)とunikernel.ml(アプリケーションのソースコード)が生成されている事を確認。

 $ cd mirage-skeleton/device-usage/network/
$  
$ ls
config.ml  unikernel.ml
$ 


コンパイル作業の前に、unikernel.mlファイルを編集しTera Term上に出力させるログのレベルをdebugからinfoに変更。
Logs.debug (fun f -> f "read: %d bytes:\n%s" (Cstruct.len b) (Cstruct.to_string b));を
Logs.info (fun f -> f "read: %d bytes:\n%s" (Cstruct.len b) (Cstruct.to_string b));に修正した。

vi unikernel.ml

open Lwt.Infix

module Main (S: Mirage_stack.V4) = struct

  let start s =
    let port = Key_gen.port () in
    S.listen_tcpv4 s ~port (fun flow ->
        let dst, dst_port = S.TCPV4.dst flow in
        Logs.info (fun f -> f "new tcp connection from IP %s on port %d"
                  (Ipaddr.V4.to_string dst) dst_port);
        S.TCPV4.read flow >>= function
        | Ok `Eof -> Logs.info (fun f -> f "Closing connection!"); Lwt.return_unit
        | Error e -> Logs.warn (fun f -> f "Error reading data from established connection: %a" 

S.TCPV4.pp_error e); Lwt.return_unit
        | Ok (`Data b) ->
          Logs.debug (fun f -> f "read: %d bytes:\n%s" (Cstruct.len b) (Cstruct.to_string b));
          S.TCPV4.close flow
      );

    S.listen s

end



通信プログラムのコンパイルとインストール
今回もVirtioオプションを付け、通信プログラムのコンパイルを実行。
コンパイル完了まで3つのコマンドを打つ必要が有る。
最初はVirtioオプションを付けてmirage configureコマンドを叩き、コンパイルに必要なファイルの生成。
オプション--ipv4でMirageOSアプリケーションにIPアドレスを設定。

$
mirage configure -t virtio --ipv4=192.168.122.100/24 --ipv4-gateway=192.168.122.1
$ 


次にmake dependコマンドを打ち、依存関係の有るopamツールおよびUbuntuのパッケージを自動でインストール。
make depend以下は自動で出力される。
今回も途中で数回質問されているようなのだが、こちらが返答する必要は無く勝手に進んでインストールされる。

$ make depend
opam pin add -k path --no-action --yes mirage-unikernel-network-virtio . && opam depext --yes --update 

mirage-unikernel-network-virtio ; opam pin remove --no-action mirage-unikernel-network-virtio
Package mirage-unikernel-network-virtio does not exist, create as a NEW package? [Y/n] y
[mirage-unikernel-network-virtio.~dev] synchronised from file:///home/hechtia/mirage-skeleton/device-

usage/network
[WARNING] Failed checks on mirage-unikernel-network-virtio package definition from source at
          file:///home/hechtia/mirage-skeleton/device-usage/network:
  warning 37: Missing field 'dev-repo'
  warning 49: The following URLs don't use version control but look like version control URLs:
              "https://github.com/mirage/mirage-skeleton.git#master"
mirage-unikernel-network-virtio is now pinned to file:///home/hechtia/mirage-skeleton/device-usage/network 

(version ~dev)
Opam plugin "depext" is not installed. Install it on the current switch? [Y/n] Y
The following actions will be performed:
  ? install opam-depext 1.1.3

<><> Gathering sources ><><><><><><><><><><><><><><><><><><><><><><><><><><><><>
[opam-depext.1.1.3] downloaded from cache at https://opam.ocaml.org/cache

<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><>
? installed opam-depext.1.1.3
:
:
:


最後に、makeコマンドを打ち通信アプリケーションバイナリファイルnetwork.virtioと仮想マシンのバイナリファイルsolo5-virtio-runを生成。
※ここで言うアプリケーションバイナリファイルとはMirageOS側、仮想マシンとはUbuntu側を指す。
生成後、lsコマンドの実行でnetwork.virtioが当フォルダ内に有る事を確認。solo5-virtio-runは別のディレクトリ内に有る。whichコマンドでsolo5-virtio-runファイルが有る場所を探す事が出来る。

$ make
mirage build
config.exe: [WARNING] pkg-config solo5-bindings-virtio --variable=ld returned nothing, using ld
$ 



通信プログラムの実行
ここでは、仮想マシンバイナリファイルsolo5-virtio-runでアプリケーションバイナリnetwork.virtioを、tap0をオプションに実行させる。
アスキーアートのロゴの出力は前回と同じ。
UbuntuとMirageOS間のリンク(mirage_br~tap0)がUPすると、[tcpip-stack-direct] stack assembled: mac=52:54:00:12:34:56,ip=192.168.122.100が出力される。

$ solo5-virtio-run -n tap0 network.virtio
+ exec qemu-system-x86_64 -cpu Westmere -m 128 -nodefaults -no-acpi -display none -serial stdio -device virtio-net,netdev=n0 -netdev tap,id=n0,ifname=tap0,script=no,downscript=no -device isa-debug-exit -kernel /home/hechtia/mirage-skeleton/device-usage/network/network.virtio
            |      ___|
  __|  _ /  |  _ / __ /
/__ / (   | | (   |  ) |
____//___/ _|/___/____/
Solo5: Bindings version v0.6.6
Solo5: Memory map: 128 MB addressable:
Solo5:   reserved @ (0x0 - 0xfffff)
Solo5:       text @ (0x100000 - 0x220fff)
Solo5:     rodata @ (0x221000 - 0x265fff)
Solo5:       data @ (0x266000 - 0x340fff)
Solo5:       heap >= 0x341000 < stack < 0x8000000
Solo5: Clock source: TSC, frequency estimate is 2297494800 Hz
Solo5: PCI:00:02: virtio-net device, base=0xc000, irq=10
Solo5: PCI:00:02: configured, mac=52:54:00:12:34:56, features=0x79bfffe7
Solo5: Application acquired 'service' as network device
2020-08-01 04:26:21 -00:00: INF [netif] Plugging into service with mac 52:54:00:12:34:56 mtu 1500
2020-08-01 04:26:21 -00:00: INF [ethernet] Connected Ethernet interface 52:54:00:12:34:56
2020-08-01 04:26:21 -00:00: INF [ARP] Sending gratuitous ARP for 192.168.122.100 (52:54:00:12:34:56)
2020-08-01 04:26:21 -00:00: INF [udp] UDP interface connected on 192.168.122.100
2020-08-01 04:26:21 -00:00: INF [tcpip-stack-direct] stack assembled: mac=52:54:00:12:34:56,ip=192.168.122.100
2020-08-01 04:27:59 -00:00: INF [application] new tcp connection from IP 192.168.122.1 on port 58154


ここで、Tera TermやPuTTYなどターミナルツールをもう1枚立ち上げ、Ubuntuにログイン。
pingを打って疎通確認してみる。

$ ping 192.168.122.100
64 bytes from 192.168.122.100: icmp_seq=1 ttl=64 time=0.056 ms
64 bytes from 192.168.122.100: icmp_seq=2 ttl=64 time=0.059 ms
64 bytes from 192.168.122.100: icmp_seq=3 ttl=64 time=0.060 ms
64 bytes from 192.168.122.100: icmp_seq=4 ttl=64 time=0.057 ms
64 bytes from 192.168.122.100: icmp_seq=5 ttl=64 time=0.058 ms
^C
--- 192.168.122.100 ping statistics ---
24 packets transmitted, 5 received, 0% packet loss, time 0.29ms
rtt min/avg/max/mdev = 0.056/0.067/0.108/0.011 ms
$
$


MirageOSまで届いた。
この状態でechoコマンドとncコマンドでMirageOSに文字列"hogehoge"を送ってみる。

$
$ echo -en "hogehoge" | nc 192.168.122.100 8080
$


この時、1枚目のターミナルツールに戻ると、出力内容の一番下の行にUbuntuからTCPコネクションが発生した旨のログが出力された。
(2020-08-01 04:27:59 -00:00: INF [application] new tcp connection from IP 192.168.122.1 on port 58154の事)
併せて、2020-08-01 04:27:59 -00:00: INF [application] read: 8 bytes:のログ出力と同時に hogehoge が出力された。

$ solo5-virtio-run -n tap0 network.virtio
+ exec qemu-system-x86_64 -cpu Westmere -m 128 -nodefaults -no-acpi -display none -serial stdio -device virtio-net,netdev=n0 -netdev tap,id=n0,ifname=tap0,script=no,downscript=no -device isa-debug-exit -kernel /home/hechtia/mirage-skeleton/device-usage/network/network.virtio
            |      ___|
  __|  _ /  |  _ / __ /
/__ / (   | | (   |  ) |
____//___/ _|/___/____/
Solo5: Bindings version v0.6.6
Solo5: Memory map: 128 MB addressable:
Solo5:   reserved @ (0x0 - 0xfffff)
Solo5:       text @ (0x100000 - 0x220fff)
Solo5:     rodata @ (0x221000 - 0x265fff)
Solo5:       data @ (0x266000 - 0x340fff)
Solo5:       heap >= 0x341000 < stack < 0x8000000
Solo5: Clock source: TSC, frequency estimate is 2297494800 Hz
Solo5: PCI:00:02: virtio-net device, base=0xc000, irq=10
Solo5: PCI:00:02: configured, mac=52:54:00:12:34:56, features=0x79bfffe7
Solo5: Application acquired 'service' as network device
2020-08-01 04:26:21 -00:00: INF [netif] Plugging into service with mac 52:54:00:12:34:56 mtu 1500
2020-08-01 04:26:21 -00:00: INF [ethernet] Connected Ethernet interface 52:54:00:12:34:56
2020-08-01 04:26:21 -00:00: INF [ARP] Sending gratuitous ARP for 192.168.122.100 (52:54:00:12:34:56)
2020-08-01 04:26:21 -00:00: INF [udp] UDP interface connected on 192.168.122.100
2020-08-01 04:26:21 -00:00: INF [tcpip-stack-direct] stack assembled: mac=52:54:00:12:34:56,ip=192.168.122.100
2020-08-01 04:27:59 -00:00: INF [application] new tcp connection from IP 192.168.122.1 on port 58154
2020-08-01 04:27:59 -00:00: INF [application] read: 8 bytes:
hogehoge





今回の振り返り。Ubuntu~MirageOS間の通信と文字列の送信に成功した。
当記事の編集前との違いは、UbuntuとMirageOSのバージョンとバージョン変更により一部のUbuntuやopamのパッケージが変わったくらいで、config.mlやunikernel.mlの内容は前回と同じ。


リンク先
https://mirage.io/ Mirage OS
https://github.com/mirage Mirage OS (github)
https://releases.ubuntu.com/20.04/ Ubuntu 20.04

https://debslink.hatenadiary.jp/entry/20200516/1589616362 前回:MirageOS Unikernelを少しかじってみた
https://debslink.hatenadiary.jp/entry/20200718/1595076105 追記:Ubuntu 20.04でMirageOS Unikernel
https://debslink.hatenadiary.jp/entry/20200726/1595768920 追記:MirageOS UnikernelでWebサーバ
https://debslink.hatenadiary.jp/entry/20200815/1597493701 追記:MirageOS Unikernelでデータベース

今回の通信テストの参考先 (MirageOS Unikernel アプリケーションの中身を理解する(その1))
https://qiita.com/t-imada/items/5d0149587d36c6051ed3

自分にとって始めの一歩となったサイト (MirageOS のインストールから Hello World までを試す)
https://qiita.com/t-imada/items/6ee299653ac063532b4f