KSMの実行可否

仮想化ホスト側で使用しているKSMだが、仮想化ゲスト側で使うべきなのかはいまいち確証が持てなかった。

が、今日KSMの起動用Systemd Unitを見直していたら下記の記述があった。

ConditionVirtualization=no

これは仮想化環境と言う事が検出されたら実行しないと言う事らしいので、SUSEの見解としては仮想化環境では使わないことを想定していると考えられる。


とりあえずホスト側でのみ実行中。


しかし、本当にsystemd多機能だな…

Intel SpeedShift

と言う訳で、Linuxが対応していなくて動いていないと思っていたIntel SpeedShiftはちゃんと動いていたっぽい。

【笠原一輝のユビキタス情報局】Skylakeの“SpeedShift”でPステートの消費電力削減を実現 ~Windows 10とSkylakeでさらなる長時間バッテリ駆動が可能に - PC Watch

ソフト制御の代わりに、ダイ内のコントローラ(PCU)が負荷状態を監視して制御する。
有効になっているかは起動時のメッセージで判別可能。

koke@koke-vm-server:~> dmesg | grep pstate
[ 3.831554] intel_pstate: Intel P-state driver initializing
[ 3.831855] intel_pstate: HWP enabled

HWPというのが、HardWare controlled P-Stateの略で、P-stateがハードウェアによってコントロールされていることを示している。

cpupower monitorで見るとC10(ほとんどの部分の電源まで遮断)まで落ちているので、ちゃんと動作している様子。

VM起動監視サービス

仮想化ホスト側の監視スクリプトを整理した。
pingによる生存確認とか、systemd-notifyによる起動完了通知なども入れて、タイミング調整のためにそこら中に入れていたsleepはほとんど居なくなった。
systemd timerを使えば無限ループじゃなくてone shotの塊として書くこともできそうだけど、とりあえず完成。


監視部本体

#!/bin/bash

# Must be root
if test "`/usr/bin/id -u`" != 0 ; then
    echo "$0: You must be root to run this script" >& 2
    exit 1
fi

# Must be give domain name & address
[ ${#} -ne 2 ] && exit 1

POLLING_SLEEPTIME=120

source /usr/local/sbin/vm-boot-halt

#echo $1
#echo $2
domain=${1}
address=${2}

exit_service ()
{
    wait_halt_vm  ${domain} ${address}
    force_halt_vm ${domain} ${address}
}

trap "exit_service" EXIT

exit_service #terminate existing VMs

while true
do
    #            domain    address
    wait_boot_vm ${domain} ${address}

    systemd-notify --ready

    sleep $POLLING_SLEEPTIME
done

マジックナンバーを外から渡すようにして、共通化

共通部分(関数定義)

#!/bin/bash

BOOT_TIMEOUT_COUNT=300 #same as sec
HALT_TIMEOUT_COUNT=300 #same as sec

#N_("no state"),
#N_("running"),
#N_("idle"),
#N_("paused"),
#N_("in shutdown"),
#N_("shut off"),
#N_("crashed"),
#N_("pmsuspended")

export LANG=C

boot_vm ()
{
    state=`virsh domstate ${1}`
    rc=${?}
    [ ${rc} -ne 0 ] && exit ${rc}

    case ${state} in
        "no state")
            echo "no state"
            rc=1;;
        "running")
            rc=0;;
        "idle")
            rc=0;;
        "paused")
            virsh resume ${1}
            rc=${?};;
        "in shutdown")
            rc=0;;
        "shut off")
            virsh start ${1}
            rc=${?};;
        "crashed")
            virsh reset ${1}
            rc=${?};;
        "pmsuspended")
            virsh dompmwakeup ${1}
            rc=${?};;
        *)
            echo "illegal state"
            rc=1
    esac
    [ ${rc} -ne 0 ] && exit ${rc}
}

wait_boot_vm()
{
    flag=1

    for i in `seq 0 ${BOOT_TIMEOUT_COUNT}`
    do
        boot_vm ${1}

        ping -c 1 ${2} > /dev/null
        rc=${?}
        if [ ${rc} -eq 0 ]; then
            flag=0
            #echo "booted"
            break
        fi
    done
    [ ${flag} -ne 0 ] && exit ${flag}
}

halt_vm ()
{
    state=`virsh domstate ${1}`
    rc=${?}
    [ ${rc} -ne 0 ] && exit ${rc}

    case ${state} in
        "no state")
            echo "no state"
            rc=1;;
        "running")
            virsh shutdown ${1}
            rc=${?};;
        "idle")
            virsh shutdown ${1}
            rc=${?};;
        "paused")
            virsh resume ${1}
            rc=${?}
            virsh shutdown ${1}
            rc=${rc}+${?};;
        "in shutdown")
            rc=0;;
        "shut off")
            rc=0;;
        "crashed")
            virsh destroy ${1}
            rc=${?};;
        "pmsuspended")
            virsh dompmwakeup ${1}
            rc=${?}
            virsh shutdown ${1}
            rc=${rc}+${?};;
        *)
            echo "illegal state"
            rc=1
    esac
    [ ${rc} -ne 0 ] && exit ${rc}
}

force_halt_vm ()
{
    state=`virsh domstate ${1}`
    rc=${?}
    [ ${rc} -ne 0 ] && exit ${rc}

    case ${state} in
        "no state")
            echo "no state"
            rc=1;;
        "running")
            virsh destroy ${1}
            rc=${?};;
        "idle")
            virsh destroy ${1}
            rc=${?};;
        "paused")
            virsh resume ${1}
            rc=${?}
            virsh destroy ${1}
            rc=${rc}+${?};;
        "in shutdown")
            virsh destroy ${1}
            rc=${?};;
        "shut off")
            echo "no destroy"
            rc=0;;
        "crashed")
            virsh destroy ${1}
            rc=${?};;
        "pmsuspended")
            virsh dompmwakeup ${1}
            rc=${?}
            virsh destroy ${1}
            rc=${rc}+${?};;
        *)
            echo "illegal state"
            rc=1
    esac
    [ ${rc} -ne 0 ] && exit ${rc}
}

wait_halt_vm()
{
    flag=1

    for i in `seq 0 ${HALT_TIMEOUT_COUNT}`
    do
        halt_vm ${1}

        ping -c 1 ${2} > /dev/null
        rc=${?}
        if [ ${rc} -ne 0 ]; then
            flag=0
            echo "safe halted"
            break
        fi
        sleep 1
    done
    [ ${flag} -ne 0 ] && exit ${flag}
}

本体のバリエーションが1つのみになったので、分ける必要はなかった気もする。

最後にsystemd unit

[Unit]
Description=KVM SUSE Tumbleweed ssh server startup
After=network.target libvirt-guests.service libvirtd.service vm-router.service
Requires=network.target libvirt-guests.service libvirtd.service

[Service]
ExecStart=/usr/local/sbin/vm-heartbeat-service tw-ssh-server 192.168.1.200
ExecReload=/bin/kill -HUP $MAINPID
Restart=on-failure
Type=notify

[Install]
WantedBy=multi-user.target

Typeをnotifyにして依存関係待ち合わせをsystemdに解決させるようにした。


仮想化サーバーで自分で作った部分はここだけなので、新規構築用の備忘録。

tuned停止

有効にしていたtunedデーモンだが、結局停止した。
virtual-hostが継承しているthroughput-performanceはCPUクロック下限を最大周波数にする(min_perf_pct=100)オプションが付いていて、DVFSが利かなくなる。
スループット最大化が目的なので、やってることは正しいのだがそこまで必要ないので無効化。


というか、cpu governorの設定にintel_pstateドライバだと使えないconservativeとか書いてあったり、rhel6と書いてあったり微妙に古い?


無効化した後、カーネルのデフォルト設定にしたらDVFSが有効になってちゃんと制御されている模様。

OPNsenseでDynamic DNS

自宅サーバーのDynamic DNSにieserver.netを使わせていただいているが、NECルーターを使っていた時にルーターで設定できなかった(対応サービスに存在しなかった)ので、クライアントのWindows上でDiCEを使っていた。
Windows10でも特に問題なく動いていたが、Windows機の電源が切れているときにIPアドレスが変わると繋がらなくなるのは流石にアレなので、仮想化ルーターに合わせて仮想化ルーター側に移動した。


OPNsenseもieserverのGnuDIPには対応していないっぽいので、別の方法を検討した。
便利ツールのページにperlスクリプトがあるので、これを適当な仮想マシンでCronで動かせばいいか…と思っていたが、よくよく見るとほとんどはIPアドレスの変化を監視する部分で、DNSエントリの更新はwget1コマンドでできるっぽい。


以下に処理部分を引用。

$STATUS = `wget -q -O - '$DDNS_UPDATE?username=$ACCOUNT&domain=$DOMAIN&password=$PASSWORD&updatehost=1'`;

どうも、このURLにアクセスするとアクセス元のIPアドレスを新しいアドレスとして認識するという仕組みらしい。


OPNsenseのDynamic DNSにはCustomというタイプがあり、これは特定のインターフェースを監視して、IPアドレスが変化したら指定されたURLにアクセスするという動作をするので、上記のURLを指定してやればよい。
しばらく使ってみたが、再起動時でIPアドレスが変化したときなども追従するので大丈夫だと判断した。
本当は上記でもやっているように戻り値をチェックするべきだとは思う。

OPNsense+virtio

pfSenseの時から、"仮想化したLinuxNICにvirtioを選ぶと通信が失敗する"、"iTunesの通信がよく失敗する"、"添付ファイル付きメールが送れない"、”Windows Updateがたまに失敗する"、"内側(dmz)側のルーターのネットワーク経由のアップデートができない"など微妙にイラっと来る問題が発生していた。
それぞれ毎回起こるわけでもなかったり、代替手段で何とかなったりしていたので、真剣に追わず、"仮想化モニタ側の問題で、今にアップデートで直るだろう"と思っていた。


本日、予期せず原因が見つかって直せたのでメモ。


OPNsenseのドキュメントを読んでいたら、仮想化環境にインストールした時の設定方法が書いてあった。
Virtual & Cloud based Installation — OPNsense documentation


まず、General TipsとしてネットワークアダプタのすべてのハードウェアオフロードオプションはOFFにせよと書いてあり、Xen上だがChecksum Offloadを無効化しないとNATに問題が出るとあった。
もしかして…と思い、標準で無効化されているTSOとLROに加え、CRCも無効化したのち、ルーターを再起動したところ上記の問題が直った。


完全に仮想化モニタ側だと思い込んでいて、ルーター側をあまり疑わなかったのが失敗だったかもしれない。
pfSenseでもほぼ同じことが起きており、ベースも同じFreeBSDなのでおそらく同じ原因だと思われる。


とりあえず、全仮想マシンNICをvirtioに変更し、動作することを確認した。
virtioは準仮想化と呼ばれるもので、実ハードのエミュレーションの代わりに専用のドライバ+ハイパーバイザ側の対応によって都合のいいインターフェースを作る仕組み。
特権/非特権間のコンテキストスイッチとデータのコピーが減るので処理が軽くなるという仕組みらしい。


virtio: Linux の I/O 仮想化フレームワーク


今までもストレージデバイスにはvirtioを使っていたが、今回NICも置き換えた。
性能などは測っていないが、同じ条件で仮想化モニタのメモリ使用量が気持ち減った。


しばらく様子を見る。

OPNsense

ソフトウェアルーターOPNsenseに変えてみた。
pfSenseがなんかゴタゴタしてるなーというのと、環境が安定すると変えたくなる悪癖。
とは言っても、仮想マシンなので新たにマシンを作って設定し、監視デーモンの設定を書き換えればOK。
何の問題もなく動作した。


SSLをLibreSSLにしてみたが、特に恩恵はわからない。
今のところ安定に動作している。