Ubuntu18.04のマシンでKVMで仮想マシンを立ててcloud-initで初期化する

環境

Intel NUC(2コア4スレッド、メモリ32GB)にUbuntu18.04をインストールしました。

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

vhost_netを使えるようにする

Virtioという仮想マシンNICのオーバーヘッドを減らすための技術があるので、それを使えるようにします。 参考: https://qiita.com/nyamage/items/34ab6d56967c74326f99

$ lsmod | grep vhost
# なにも表示されない
$ sudo modprobe vhost_net
$ echo "vhost_net" >> /etc/modules
$ lsmod | grep vhost
vhost_net              24576  0
vhost                  45056  1 vhost_net
tap                    24576  1 vhost_net
# カーネルモジュールがロードされた

KVM関連パッケージをインストールする

$ sudo apt install qemu-kvm bridge-utils libvirt0 libvirt-bin virtinst libguestfs-tools

ネットワーク設定

VMNICを接続するブリッジを作成します。Ubuntu18.04なので /etc/network/interfaces ではなく、netplanの設定を書きます。 手元環境だと /etc/netplan/01-netcfg.yaml を編集しました。

network:
  version: 2
  renderer: networkd
  ethernets:
    eno1:
      dhcp4: no
      dhcp6: no
  bridges:
    br0:
      interfaces:
        - eno1
      dhcp4: no
      dhcp6: no
      addresses:
        - 192.168.10.2/24
      gateway4: 192.168.10.1
      nameservers:
        addresses:
          - 8.8.8.8
      parameters:
        stp: no
      optional: yes

今回はサーバの物理NICにルータが接続されていて、ルータがNATしてインターネットに出ていく構成のため、サーバ側ではNATをしていません。必要に応じてiptablesでNATの設定を書くなりしてください。

仮想マシンを起動してみる

Ubuntu Cloud Imageを入手する

Ubuntuマシンが起動したかったのでUbuntuを例にします。UbuntuはCloudで起動する用のマシンイメージが配布されています。ISOイメージからインストール作業をしてもいいのですが、せっかくなので配布されているQCOW2形式のイメージを使います。

$ wget https://cloud-images.ubuntu.com/releases/18.04/release/ubuntu-18.04-server-cloudimg-amd64.img -O ubuntu-18.04-server.qcow2

VM用ディスクを作成する

ダウンロードしてきたイメージファイルからそのまま起動してもいいのですが、VMを作るごとにベースイメージをコピーしないといけなくてディスク容量を無駄に消費してしまいます。QCOW2形式はCopy On Writeなイメージなので、差分ディスクが作成できます。

$ qemu-img create -b ubuntu-18.04-server.qcow2 -f qcow2 v01.qcow2 8G
Formatting 'v01.qcow2', fmt=qcow2 size=2361393152 backing_file=ubuntu-18.04-server.qcow2 cluster_size=65536 lazy_refcounts=off refcount_bits=16

$ ls -lh
total 324M
-rw-rw-r-- 1 kuro kuro 324M Oct 30 01:53 ubuntu-18.04-server.qcow2
-rw-r--r-- 1 kuro kuro 193K Nov  3 15:21 v01.qcow2

v01.qcow2 は193KBしかないことがわかりますね。差分ディスクなのでOSを起動してベースイメージから差分が発生すればするほどファイルサイズが大きくなっていきます。

cloud-initのmeta-dataとuser-dataの作成

このままでもVMは起動できるのですが、Cloud Imageはcloud-initという仕組みを使って起動時にユーザアカウントなどの初期化処理が行われます。KVM単体ではcloud-initの仕組みには対応していないので、VMが起動できてもユーザ設定がされていないためログインができません。 ディスクを直接編集してrootパスワードを変更しておく方法もありますが、せっかくなのでcloud-initの仕組みを応用することにします。 ディスクを直接編集する場合はこちら: https://qiita.com/s1061123/items/fa6d54ef6705135e5969

user-dataというファイルとmeta-dataというファイルを作成します。user-dataの先頭行の#cloud-config という文字列は必須です。その2つのファイルが入ったisoイメージファイルを作成します。

$ cat user-data
#cloud-config
password: ubuntu
chpasswd: {expire: False}
ssh_pwauth: True

$ cat meta-data
instance-id: ubuntu01
local-hostname: ubuntu01
   
$ genisoimage -output userdata.iso -volid cidata -joliet -rock user-data meta-data

参考: https://access.redhat.com/ja/articles/1460743

VMの作成

起動させず、作成のみ行いたい場合は --noreboot をつけます。仮想マシンのディスクのほかにcloud-initの情報が入ったisoイメージをマウントしていてこの情報を使って初期化されます。

$ sudo virt-install --name v01 --ram 1024 --vcpus 2 --arch x86_64 --os-type linux --os-variant ubuntu16.04 --hvm --virt-type kvm --file /home/kuro/kvm/ubuntu/v01.qcow2 --cdrom /home/kuro/kvm/ubuntu/userdata.iso --boot hd --network bridge:br0 --graphics none --serial pty --console pty --autostart --noreboot
    
WARNING  CDROM media does not print to the text console by default, so you likely will not see text install output. You might want to use --location. See the man page for examples of using --location with CDROM media
    
Starting install...
Connected to domain v01
Escape character is ^]
[    0.000000] Linux version 4.15.0-38-generic (buildd@lcy01-amd64-023) (gcc version 7.3.0 (Ubuntu 7.3.0-16ubuntu3)) #41-Ubuntu SMP Wed Oct 10 10:59:38 UTC 2018 (Ubuntu 4.15.0-38.41-generic 4.15.18)
...

VMの起動

$ sudo virsh start v01

VMのコンソールにアタッチする

正常に起動できていれば、コンソールにアタッチできているはずです。起動中であれば起動中のログが流れてきますし、そうでなければEnterキーを押せばログインプロンプトが出てくるでしょう。

$ sudo virsh console v01
Connected to domain v01
Escape character is ^]
    
Ubuntu 18.04.1 LTS ubuntu01 ttyS0
    
ubuntu01 login: ubuntu
Password:
Last login: Sat Nov  3 07:59:49 UTC 2018 on ttyS0
Welcome to Ubuntu 18.04.1 LTS (GNU/Linux 4.15.0-38-generic x86_64)

初めてKVMを触ったのですが、思ったより簡単でした。

情報科学若手の会に参加しました/運営をしました/幹事とは #wakate2018

第51回 情報科学若手の会に参加してきました。
そして今年は自分が情報科学若手の会の代表幹事になりました。 無事に終えることができてよかったですし、楽しかったです。
最後に若手の会の記事を書いたのは2年前かつ会社のブログだったので、今回はこっちに書いてみます。

adtech.cyberagent.io

どんなことをやったのか

情報科学若手の会はこんな会です。 https://wakate.org/about/

招待講演、若手特別講演は上記のような感じでした。

一般参加者からの発表はざっくりテーマ的にはこんな感じでした。

詳細は開催報告で書くのでそちらに…(早めにアップします。。)
twitterハッシュタグ #wakate2018 でツイートは追えます。

twitter.com

(会社の元同期でもある)κeenの各プログラミング言語での非同期処理の表現方法についての発表は自分が業務で扱っている技術的対象でもあるので知識の整理になってよかったです。

Futureとその周辺 | κeenのHappy Hacκing Blog

早坂くんの発表はSegmentRoutingについてで、言葉は耳にしたことがあるもののどんなものなのか全く知らなかったので周辺知識と一緒に学ぶことができました。

Segment Routingの利用例と未来 - Speaker Deck

今回の開催地

加藤山崎教育基金の軽井沢研修所にて開催しました。

www.kato-karuizawa.jp

f:id:kuro_m88:20181006131120j:plain

過去6年間は伊東の山喜旅館で開催していましたが、今年は場所を変えてみました。 大研修室は広くて使いやすく、プロジェクターが最新の強いモデルになっていたのでスライドの視認性が高くてよかったです。日程がもうちょっと遅いときれいな紅葉が見られそうです。

情報科学若手の会の幹事とは

幹事を選出する基準は特に決まっていませんが、後述する情報科学若手の会の規約のとおり幹事会によって選出されます。僕は3年前の若手の会の途中に当時の幹事に呼び出されて幹事にならないかと声をかけられ今ココという感じです。 規約は3年くらい前からGitHub上で管理されていて、変更をするにはプルリクエストを投げます。イマドキですね。

github.com

最近では幹事の承認はプルリク上のapproveですし(幹事会をやっている時や意味の変更を伴わない修正は省略することもあります)、プルリクがマージされるとCIによってPDFが自動生成されます。

f:id:kuro_m88:20181009233221p:plain

幹事をやることで金銭的なメリットはないです。一般参加者と同様に参加費を支払って参加しています。そういう意味もあってタイトルには参加しましたと書きました。
幹事も一参加者なので、幹事も楽しめないといけません。幹事が事務作業に追われていて全員が発表を聴けない時もあったそうですが、改善や情報処理学会のご協力により負担が軽減され、今では全員セッションに参加できています。とはいえ幹事も一般発表したりのんびりする暇があるかというと正直厳しいところがあるので事前準備を頑張るとかしていきたいです。

なぜ幹事をやるのかは人によって違うかもしれません。少なくとも自分の場合は若手の会に参加したおかげでたくさん刺激と影響を受けたので、こういうコミュニティに貢献したいという気持ちがあるのでやっています。あと、幹事のメンバーでわいわい企画して運営するのも楽しいです。

お金のはなし

今回、参加者に情報科学若手の会の予算についてどうなっているのか聞かれることがちょくちょくあったので簡単にどうなっているのか書いてみます。
収入は参加者からの参加費と、スポンサー企業からのスポンサー費と、主催である情報処理学会のプログラミングシンポジウム委員会からの補助です。
今年の参加募集ページをご覧頂くとわかりますが、参加費は2泊3日、食費と懇親会費もろもろ込みです。安いと感じられる方が多いのではないでしょうか。学生は格安、社会人も気軽に参加できる程度を目標にしています。 開催地もその点を考慮して選んでいて、費用と環境のバランスが取れる場所を選定しています。
最終的に残った額は全額学生への交通費補助に充当しています。翌年へ予算の繰越はしていません。

情報科学若手の会はどういう会なのか

国会図書館に行って調査したのですが、第8回(43年前!)の参加報告書にはこんなことが書かれていました。

f:id:kuro_m88:20181009233238p:plain

当時は大学卒業後6年以内が若手の会の参加資格だったようです。

f:id:kuro_m88:20181009233253p:plain

今では参加資格を「若手」とあえて明確には定めておりませんが、基本的なスタンスは「学会」「研究会」としています。しかし学会ほど堅い雰囲気もなく、また情報科学という非常にざっくりしたテーマにしているので様々な分野を専門にしている人が集まります。今年は高校生が2名参加して、それぞれショートセッションで発表してくれました。 若手の会ですから、参加者が固定されてしまっては全員一緒に歳を重ねてしまい、気がついたころには若手ではなくなってしまうので常に新規の参加者を集めなければなりません。研究室選びの悩みはもちろん、情報科学若手の会で先輩に相談した結果博士課程まで進学したという人も居るわけで、そういった先輩後輩の相談の場でもあります。そういった場を提供するためには初学者の若手の会への参加ハードルを下げるような工夫や参加しやすい雰囲気づくりをしなければなりません。 一方で情報処理学会の組織の一部であり、「学会」「研究会」のような側面も持ち合わせていますから、最先端の研究の発表やそれなりの基礎知識を前提とする深い発表もされます。 必ずしも全員が理解できる内容ではなく、チュートリアル的な発表もあれば参加者の一部しか完全には理解できない発表もあります。登壇者に対して幹事から特に発表内容の技術的/知識的難易度は指定しておらず、完全にお任せしています。そのおかげで登壇者は話したいこと、参加者と議論したい内容を自由に持ち込んでもらい好きなように時間を使ってもらえているのですが、参加を迷っている人からするとそれがハードルに感じさせてしまっているかもしれません。また、初参加の方はどのような聴衆を想定すればいいのか迷うかもしれません。

ルールなく自由にするのがいい面と悪い面はあるのでここのバランスやどのような立場を取るのかは継続的に話し合っていくようにしたいですね。

これからやること

まずは会計処理等事務的な手続きをして、反省会です。今年は幹事が3人入れ替わったのと開催地を替えてみたこともあり不慣れなことが多かったです。アンケートを読み返したり振り返りをしつつ来年どうするかを幹事で話し合います。その後来年1月のプログラミングシンポジウムで開催の報告をしたら第51回は終了です。 第52回ももちろんやるつもりなので、準備は来年の春くらいからやりましょうかね。

非公式イベントではありますが、情報科学若手の会 冬の陣という1日限定イベントもここ数年は開催しているので今年もやるのかどうかこれから検討します。

wakate.connpass.com

感想とか

情報科学若手の会に初めて参加したきっかけは6年前の夏のことです。 当時は大学2年生でした。大学の授業のTAをやっていたとある先輩からメールで誘われてよくわからないまま参加する言ったのがきっかけです。

f:id:kuro_m88:20181009233349p:plain

読み返してみると思ったより軽いやりとりで、これだけの情報でどうして行こうと思ったんだろうかと思ってしまいました(笑)まぁ、先輩に誘われたからですね。この先輩はすごくて、たしか僕が初参加した年は「ひとりぼっちをなくすこと」を目標に動いていた気がします。初参加だったもので懇親会で浮き気味だったのですが、いろんな参加者を紹介して繋いでくれて、おかげでとても楽しかった記憶があります。ありがとうございます。

気がつけば4回目の参加から幹事になり、今回の7回目(2015年は海外出張が被ったのでオンラインでちょこっと顔を出しただけ)の参加にして代表幹事になってしまいました。だからといって大きく変わったこともなく、今回も楽しかったです。台風がきた時の対処を考えている時はドキドキでしたね。晴れてよかった。 伊東から軽井沢に移り、海から森にきたので緑が豊かでした。

f:id:kuro_m88:20181008131629j:plain f:id:kuro_m88:20181008130111j:plain

キノコがたくさん生えていて探すのが楽しかったです。キジが生息しているそうですが僕は出会えませんでした(´・ω・`)

発表はどれも楽しかったし、勉強になるものばかりで発表/参加してくださった皆様には感謝です。

今年も情報科学若手の会で出会うようなすごい人達に追いつけるようにがんばろうと思ったのでした。

また来年お会いしましょう!

告知

情報科学若手の会の派生元である、情報処理学会 「第60回プログラミング・シンポジウム」が2019年1月11日〜1月13日の3日間、伊東にて開催されます。 若手の会と違い様々な年代の方が参加されますし、先生方も多くいらっしゃるので若手の会よりはアカデミック色が強めです。今年の1月のプログラミングシンポジウムに初めて参加しましたが、こちらも楽しかったです。

www.ipsj.or.jp

Havitのキーボード「HV-KB390L」のレビュー

以前キーボードのレビュー記事を書いたのですが、 ( 左右分離型のキーボードMistel Barocco MD600が販売再開されたみたいなので買った - くろの雑記帳 )その後、 Havit社の方からウチのキーボードのレビューも書いて欲しいと依頼を受けました。 会社で1ヶ月間Havitのキーボードを利用してみたので、そのレビューを書きます!

どんなキーボード?

Havitは1998年に中国で創業したPC/モバイル周辺機器のメーカーのようです。 HV-KB390Lはロープロファイルのメカニカルスイッチを採用したコンパクトなキーボードです。LEDの装飾付き。

開封の儀

パッケージはこんな感じです。いたってシンプル。 f:id:kuro_m88:20180507112021j:plain

箱をあけてみました。シンプルでコンパクトなキーボードですね。クセはなさそう。

f:id:kuro_m88:20180507112158j:plain

PCに接続してみました。青く光っています!(写真じゃ分かりづらいか…) f:id:kuro_m88:20180507112840j:plain

光る!

キーボードの光らせ方がデフォルトで7種類用意されています。また、Fn + F12キーを押すと学習モードになり、自分で設定した固定のキーのみを光らせる事も可能。

一番気に入ったモードは以下の動画のやつです。

叩いたキーから波紋状に光が広がっていくのがかっこいいですね。慣れるまではLEDがチカチカするのが気になってタイピングしにくかったですが、数日使っていると光っていても気にならなくなってきます。が、会社で使っていたので通り掛かる人には「なにこれ!?」と声を掛けられます。まぁ目立ちますよね。

使われているキーは?

ESCキーを取り外して撮影してみました。 f:id:kuro_m88:20180507182841j:plain

Kailh社のロープロファイルキースイッチが使われています。青軸です。日本だとメカニカルキースイッチだと、CherryのMXシリーズが一番有名だと思いますが、このキーはCherry MXのキーピッチを低くしたような感じです。 青軸なので、クリック感があってカチャカチャ言うタイプですね。さきほどの動画を音声ありで聞いていただければ音がわかると思います。 Cherryの青軸だったら正直うるさいので会社で使うと隣の人に迷惑かな…?と気にしたりしますが、キーピッチが低いせいもあるのか、Cherryの青軸よりはわりと静かです。会社の雑音の中だとこのキーのクリック音程度では迷惑になるほどの音量ではなさそうです。 キーピッチが低めな事に関しても使いにくいのではないかと思っていたのですが、特に気になりませんでした。仕事でPCはMacBook Proを使っているのですが、あの極薄キーボードに慣れている身からすると十分深いですし、Cherry MXでは深すぎてOリングを挟んでキーピッチを浅くしていたのでちょうどいいです。

使ってみてどうだったか

青軸はゲーム用だから仕事には向かないよね、と敬遠していたところもあったのですが、仕事で使ってみると案外打鍵感が気持ちよくて楽しかったです。作りもしっかりとしていて、押し込んでもたわんだり変なゆがみを感じたりしません。安定感があります。キーを斜めに押し込んだ時に引っかかりを感じるキーボードは雑に高速に打鍵してる時にストレスがたまるのですが、わざと斜めに入力してみてもスムーズでした。 キー配列も特にクセのないUS配列で、矢印キーも独立しているのでメカニカルキーボードデビューしてみたい人におすすめです。(日本語配列のリンクは最後にあります)ゲーミングキーボードとして売られていますが特にゲームにしか向いていないなと思う点もありません。Windows用の専用ソフトを使うとキーの入れ替えやマクロの設定ができるようです。 普段はMISTELのMD600を使っている( 左右分離型のキーボードMistel Barocco MD600が販売再開されたみたいなので買った - くろの雑記帳 参照)のですが、このキーボードはFnキーとの組み合わせで音量や再生中の音楽の一時停止、曲送りなどが操作できて、よく使っているのでこの操作ができないのは不便に感じました。 日本のAmazonで検索してみると( https://amzn.to/2JyszA4 )、7999円(2018/06/11時点)とメカニカルキーボードの中ではお手頃な価格だと思います。

本日紹介したキーボード

公式サイトはこちら: www.prohavit.com

日本で購入する場合はAmazonで買うのが楽かなと。日本語配列も発売されたようです。

Akka Httpの中で使われているFastFutureがおもしろかったので紹介

akka-httpの中で使われている FastFuture が面白かったので紹介します。 Scalaの標準の scala.concurrent.Future と基本的な挙動は同じですが、パフォーマンス面で有利になるような実装になっています。

既存のFutureのどこがパフォーマンス的に不利なのか

Future(123).map(_ * 2).map(_ + 234)

のような処理があった時、123 を生成するスレッドと、その値を2倍するスレッドと、 234 を加算するスレッドが別になる。これは flatMap にも言えて

for {
  a <- Future { calculateA() }
  b <- Future { calculateB(a) }
  c <- Future { calculateC(b) }
} yield c

みたいな処理があった時、 calculateA, calculateB, calculateC 3つのメソッドが別のスレッドで実行される。 map でチェーンするのも、flatMap をチェーンさせる(for文)のも Future が作られるもののこの書き方だと直列に実行されるので別スレッドを割り当てるのはコンテキストスイッチを発生するのとCPUのキャッシュが汚染されるのでパフォーマンス的に無駄がある。 型をあわせるだけの目的であれば Future.successful を使えばスレッドが割り当てられないので型をあわせるだけならこれが使われることが多い。 しかしながら、実は Future#map を呼ぶと内部で別スレッドの割当が行われるので Future.successful を使っていてもスレッドの切り替わりは完全に防げているわけではない。

例えば、 scala.concurrent.Future の実装

  def map[S](f: T => S)(implicit executor: ExecutionContext): Future[S] = { // transform(f, identity)
    val p = Promise[S]()
    onComplete { v => p complete (v map f) }
    p.future
  }

map した中身を処理するときに新たに Future を生成しているのでスレッドの切り替わりが発生している。

akka.http.scaladsl.util.FastFuture

実装

https://github.com/akka/akka-http/blob/master/akka-http-core/src/main/scala/akka/http/scaladsl/util/FastFuture.scala

implicit class EnhancedFuture[T](val future: Future[T]) extends AnyVal {
  def fast: FastFuture[T] = new FastFuture[T](future)
}

このimplicitを使って Future#fast を呼べば FastFuture が作れる。

Future(123).fast
=> FastFuture[Int]

FastFuture の一番のポイントは以下で

private case class FulfilledFuture[+A](a: A) extends Future[A] { ... }
private case class ErrorFuture[+A](a: A) extends Future[A] { ... }

def map[B](f: A ⇒ B)(implicit ec: ExecutionContext): Future[B] =
  transformWith(a ⇒ FastFuture.successful(f(a)), FastFuture.failed)

def transformWith[B](s: A ⇒ Future[B], f: Throwable ⇒ Future[B])(implicit executor: ExecutionContext): Future[B] = {
  def strictTransform[T](x: T, f: T ⇒ Future[B]) =
    try f(x)
    catch { case NonFatal(e) ⇒ ErrorFuture(e) }

  future match {
    case FulfilledFuture(a) ⇒ strictTransform(a, s)
    case ErrorFuture(e)     ⇒ strictTransform(e, f)
    case _ ⇒ future.value match {
      case None ⇒
        val p = Promise[B]()
        future.onComplete {
          case Success(a) ⇒ p completeWith strictTransform(a, s)
          case Failure(e) ⇒ p completeWith strictTransform(e, f)
        }
        p.future
      case Some(Success(a)) ⇒ strictTransform(a, s)
      case Some(Failure(e)) ⇒ strictTransform(e, f)
    }
  }
}

transformWith メソッド内で Future を継承した FulfilledFuture もしくは ErrorFuture であれば Future ではなく Try として実行される。それ以外(ふつうのFuture)であれば通常の Future と同じ挙動をする。

いいこと

Future#fast を呼ぶだけで FastFuture が作れて、これを使うと別スレッドの割り当てが発生しないので直列に実行される場合の Future のチェーンにおいて無駄なコンテキストスイッチが発生せずパフォーマンス面で効率がよい。

わるいこと

サービス層とリポジトリ層で ExecutionContext を分けている場合に意図的に割り当てるスレッドプールを変えたいような場合でもスレッドが切り替わらないのでimplicitで ExecutionContext を渡してあげても意図したとおりにスレッドが変わらない。

scala.concurrent.Futureに取り込まれないのか?

議論はありました。が、 map だけ特別扱いするのはおかしいということになり、取り込まれてはいません。 https://contributors.scala-lang.org/t/upates-to-scala-concurrent-future-wrt-breaking-changes/1281/26