bit

VirtualBox ネットワーク設定

(12/10) 再度見てみたら、NAT Networkのところも問題なく接続できていた。確認方法がいまいちなだけだった。というわけで、この記事はあたかも最初からうまく設定できてたように書き直す。
# 嘘はついていない。あくまで確認が不足していただけ…。

(3/8) バージョン4.3.8から、ping proxyが実装されているので、NAT Network でもpingが通るようになっていた。なんかこの記事、追記が多すぎるので書き直したい。


とにかくVirtualBoxのネットワーク設定である。構築に使用したOS やVirtualBoxのバージョンは以下の通り。

ホストOS
Windows 8.1
ゲストOS
CentOS 6.5
VirtualBox
4.3.4 r91027 *1

一つのホストOS上に以下のような仮想ネットワークを構成する。
f:id:iwsttty:20131210042402p:plain
想定としては、管理用セグメントをHost-Onlyネットワークで、サービス用セグメントを「NAT Network」ネットワークで、サーバ間セグメントをInternalネットワークで実現し、おのおの複数NICを持つサーバというか仮想PCを接続しようとしている。NATは本当は作るつもりなかったけど、最初はNAT Networkがうまく疎通できないと思っていた関係で手っ取り早くインターネットへの口がほしかったのと、全部使った感がほしかったので入れてみた。
疎通可否を表にするとこんな感じ (日曜日なのに仕事っぽいな)。

NAT(w/o port forwarding) NAT Network(w/o port forwarding) Host Only ネットワーク Internal ネットワーク
ゲストOS間 -
ゲストOS → ホストOS/GW -
ゲストOS → インターネット - -
ホストOS → ゲストOS - - -
DHCP disabled disabled disabled N/A

○は疎通OK、ハイフン(-)は仕様上通らないもの。DHCPは全部OFF(実際のサーバ構築では、そんなもの使わない)。ポートフォワーディングは後回し。

順番に概要だけ説明。

NAT

今のところゲストOSからインターネットに出ていくための唯一のインターフェイス
設定上の注意点は、VirtualBox が 10.0.N.0 のネットワークアドレスを勝手に割り当てること。自由なアドレスは使えない(仕様) (12/12追記 じゃなくてデフォルト値だった)。したがってゲストOS 側はこれに合わせて設定を入れる必要がある。今回、図で言うところの仮想PC O00の三番目のネットワークインターフェイスLinuxから見るとeth2)をNATにしたので、10.0.2.0から数えて三番目の 10.0.4.0 が自動的に割り当てられた。ゲートウェイは 10.0.4.2 になるので、これをデフォゲにする。このNATネットワークはちゃんと設定すれば、ゲストOS側から簡単にインターネットに疎通してくれる。

[root@vso00 ~]# ping -c 3 8.8.8.8
PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
64 bytes from 8.8.8.8: icmp_seq=1 ttl=43 time=43.6 ms
64 bytes from 8.8.8.8: icmp_seq=2 ttl=43 time=43.2 ms
64 bytes from 8.8.8.8: icmp_seq=3 ttl=43 time=43.1 ms

--- 8.8.8.8 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2047ms
rtt min/avg/max/mdev = 43.116/43.355/43.670/0.334 ms
Host-only ネットワーク

最初、ゲストからホストへは疎通せず悩んでいたが、ホストOSのWindowsにて「ゲストまたはパブリックネットワーク」側のWindowsファイアウォールを落としたら通ることが分かった。ファイアウォールを無効にするのは嫌なので、受信規則を追加した。すなわち、「スコープ」の「ローカルIPアドレス」「リモートIP アドレス」ともに「192.168.0.0/24」とし、詳細のプロファイルは「パブリック」をチェック、「操作」は「接続を許可する」。これで想定どおり動くようになった。

(3/1追記: 一つ書き忘れた。下の VBoxManage.exe list hostonlyifs の出力にあるけど、VirtualBox が生成するHost-onlyネットワーク用仮想NICIPアドレスは、Host-onlyネットワークのネットワークアドレスに合わせてデフォルトのものから変更している(GUIのファイル→環境設定→ネットワーク→Host-Onlyネットワーク→ドライバーアイコン)。これをやらないと、ホストからゲストにルーティングしてくれない。)

Internal ネットワーク

特に問題なし。寝ながら設定してもつながる。

NAT Network ネットワーク / NAT サービスネットワーク / NatNetwork

GUIでは「NAT ネットワーク」と書かれているが、マニュアルはNATサービス、どっちが正式名称なんだろう。とりあえず、GUIに合わせるが、この名称はググりにくい…。
それはともかく、NatNetwork は、Host-Onlyネットワークの機能を持ちながらも、インターネットに出ていけるネットワークである。
最初、うまく接続できないと思っていたが実はできていた。マニュアルには以下のように書いてある。

but letting systems inside communicate with each other and with systems outside using TCP and UDP over IPv4 and IPv6.
Chapter 6. Virtual networking

ICMPでつながるとはどこにも書いてなかった。不覚。(追記: 上述のとおり、4.3.8 以降は ping proxyが実装されている)

[root@vsw01 ~]# ping -c 3 www.yahoo.co.jp
PING www.g.yahoo.co.jp (203.216.231.189) 56(84) bytes of data.

--- www.g.yahoo.co.jp ping statistics ---
3 packets transmitted, 0 received, 100% packet loss, time 12001ms

[root@vsw01 ~]# curl http://www.yahoo.co.jp | head -10
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<meta http-equiv="content-style-type" content="text/css">
<meta http-equiv="content-script-type" content="text/javascript">
<meta name="description" content="日本最大級のポータルサイト。検索、オークション、ニュース、天気、スポーツ、メール、ショッピングなど多数のサービスを展開。あなたの生活をより豊かにする「課題解決エンジン」を目指していきます。">
<meta property="og:title" content="Yahoo! JAPAN">
<meta property="og:type" content="article">
<meta property="og:url" content="http://www.yahoo.co.jp/">
101  4774    0  4774    0     0  78196      0 --:--:-- --:--:-- --:--:--  172k
curl: (23) Failed writing body (3418 != 7300)

以上。とりあえず、VirtualBox の設定をさらしておく。

PS ...\virtualbox> & 'C:\Program Files\VirtualBox\VBoxManage.exe' list hostonlyifs
Name:            VirtualBox Host-Only Ethernet Adapter
GUID:            d2098a99-d909-4084-a50d-c0c828cc8a49
DHCP:            Disabled
IPAddress:       192.168.0.1
NetworkMask:     255.255.255.0
IPV6Address:     XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX
IPV6NetworkMaskPrefixLength: 64
HardwareAddress: 08:00:27:XX:XX:XX
MediumType:      Ethernet
Status:          Up
VBoxNetworkName: HostInterfaceNetworking-VirtualBox Host-Only Ethernet Adapter

PS ...\virtualbox> & 'C:\Program Files\VirtualBox\VBoxManage.exe' list intnets
Name:        InternalNetwork
Name:        intnet
(使っているのは上のほう)

PS ...\virtualbox> & 'C:\Program Files\VirtualBox\VBoxManage.exe' list natnets
NetworkName:    ServiceNetwork
IP:             192.168.2.1
Network:        192.168.2.0/24
IPv6 Enabled:   No
IPv6 Prefix:
DHCP Enabled:   No
Enabled:        Yes
loopback mappings (ipv4)
        127.0.0.1=2

Linuxのルーティング設定や/etc/sysconfig/network-scripts/ifcfg-* は、基本通り書いているだけなので詳細は省略。

手順だけ書いておく。まずVirtualboxGUIで各仮想PCのネットワークインターフェイスをNATにするとか、Host-Onlyにするとか種類を設定する。このとき、MACアドレスを確認しておく。仮想Linuxを起動、「ip link」コマンドで、設定したインターフェイスが仮想OSに認識されていることを確認。/etc/udev/rules.d/70-persistent-net.rules を見て、MACアドレスNIC名の対応付ルールに想定外のものが自動生成されている場合は適宜変更する(ATTR{address}とNAMEの対応のこと)。特にVirtualBox側でMACアドレスを再生成した場合にeth4 とかができてしまうので、例えば古いeth1行を削除し新しいeth4のNAMEをeth1 にしたりする作業が必要。変更した場合、仮想OS再起動。んで、/etc/sysconfig/network-scripts/ifcfg-ethN を修正する。見るのはBOOTPROTO(dhcpになっていないこと)、HWADDR、IPADDRとNETMASK。HWADDRがVirtualBoxGUI で設定した値と違うと、NICが起動しない。大事。外に出ていけるインターフェイスだけはGATEWAYも設定。あとはまあ「ifdown ethN; ifup ethN」して「ip addr」してpingで確認という感じ。(今回、このping確認が仇となったわけだが)
ちなみに最初の、ホストOS側のGUIで設定する部分のうち必要分は全部 VBoxManage コマンドで実施できることは確認済み。

*1:2/28追記: NAT Network について言うと、4.3.6はダメ。4.3.8はOK。

WindowsのVIMで開発しているスクリプトをリモートのLinuxで動作確認するために半自動でSCPする

というありがちなパターン。

WinSCPがインストールされている前提で、その keepuptodate コマンドでもいいんだけど、今回は反映タイミングは制御したかったので、初VIMスクリプトの練習として書いてみた。

といっても、パスセパレータのエスケープが面倒だったんで、この記事の隠れ主題として、VIMスクリプトの中でセパレータをどこで何個書けばよいかの備忘録というのがある。

下記スクリプト呼び出されている WinSCP スクリプト(sync.winscp)はリモートに接続して、syncしたりputしたりしてclose→exitするものであれば何でもよく、このVIMスクリプトと同じディレクトリに置いておくように記載している。

" Sync
let s:winscp = 'C:\\Program Files (x86)\\WinSCP\\WinSCP.exe'
let s:script_filepath = expand('%:h') . '\sync.winscp'

function! Sync()
  let l:script_filepath = substitute(s:script_filepath, '\\', '\\\\', 'g')

  let l:command =
\   printf('call system("\"%s\" /console /script=\"%s\"")',
\   s:winscp, l:script_filepath)

  execute l:command
endfunction

command! Sync :call Sync()

んで、VIMを起動したときに最初にこのスクリプトを source しておくと、開発したスクリプトをリモートのLinuxに持っていきたいときに :Sync とするだけでそれができる。

(追記)
Windowsでも、Cygwinなどの scp.exe がインストールされていてパスが適切に設定されていれば、:edit scp://~ でリモートのファイルを透過的に編集できるらしい。まあいいや。VIMスクリプトのいい練習になったのでよしとする。

部分クイックソート

久しぶりに勉強。配列の一部だけをソートする部分ソート(partial sort)。

参考にしたのは、ぐぐって見つけた論文と、ロゼッタコードのページ。たぶんこれでいいと思うけど。

sortRecursively(array, 0, array.length - 1, lowerBound, upperBound);


private static <T extends Compatible<T>>
sortRecursively(T[] array, int leftEnd, int rightEnd, int lowerBound, int upperBound) {

    int pivotIdx = selectPivot(array, leftEnd, rightEnd);

    int[] lrIdx = splitArrayIntoLandU(array, leftEnd, rightEnd, pivotIdx);

    if (lowerBound < lrIdx[0]) {
      sortRecursively(array, leftEnd, lrIdx[0], lowerBound, upperBound);
    }

    if (lrIdx[1] < upperBound) {
      sortRecursively(array, lrIdx[1], rightEnd, lowerBound, upperBound);
    }
}

private static <T extends Comparable<T>>
int[] splitArrayIntoLandU(T[] array, int leftEnd, int rightEnd, int pivotIndex) {

    T pivot = array[pivotIndex];

    while(leftEnd<= rightEnd) {
      while (array[leftEnd].compareTo(pivot) < 0) {
        ++leftEnd;
      }

      while (pivot.compareTo(array[rightEnd]) < 0) {
        --rightEnd;
      }

      if (leftEnd <= rightEnd) {
        swap(array, leftEnd, rightEnd);
        ++leftEnd;
        --rightEnd;
      }
    }

    return new int[] {rightEnd, leftEnd};
 }

これで lowerBound ~ upperBound の間だけがソートされる。メソッド selectPivot は leftEnd と rightEnd の間からピボットを取るようにする。

Rubyエンコーディング

Rubyを時々触るのだけれど、こんなプログラム:

# -*- coding: utf-8 -*-
puts "あいうえお"

を組んで、実行すると

PS D:\home\iwsttty\> ruby .\puts_japanese.rb
縺ゅ>縺・∴縺

こうなる。で、あれ?と思って、ああ「-U」オプション忘れてたと気づく。国産言語なのに、こんな単純なプログラムでさえ、日本語をJavaみたいによろしくやってくれない。
くやしいので、なぜこうなるのかというところを理解するために、とりあえず図にする。

左側が書き込みのとき、右側が読み込みのとき。矢印は実線と破線の二種類があって、前者はそのままバイト列が渡されること、後者は何らかの変換がされることを表わしている。
書き込みのときは、外部エンコーディングが明示的に設定されているかどうかで挙動が異なる(内部エンコーディングは関係ない)。設定されていなければ、Stringオブジェクトのバイト列がそのまま出力される。設定されていれば、文字列エンコーディングからそのIOオブジェクトの外部エンコーディングに変換されて出力される。明示的と言ったのは、EncodingのメソッドにEncoding.default_externalというのがあって、外部エンコーディングが設定されていないとあたかもこちらの値が使われるようなイメージがあるからであるが、書き込み時はこの値は関係ない。
読み込みのときは、内部エンコーディングが設定されているかどうかで挙動が異なる。設定されていなければ、入力されたバイト列は外部エンコーディングを仮定して、Rubyプロセス内部に読み込まれる。設定されていれば、さらに内部エンコーディングに変換される。
先ほどのEncoding.default_externalは、読み込み時で、かつそのIOオブジェクトに外部エンコーディングが設定されていない場合に用いられる。ニッチだ。
冒頭のプログラムに戻ると、Rubyは何もしないと標準出力オブジェクトSTDOUTのエンコーディングは内部、外部ともに設定されないため、UTF-8文字列を外部に出力すると、そのままPowerShellの端末に出力される。PowerShellのエンコーディングはディフォルトでは「日本語 (シフト JIS)」なので、文字化けする。

PS D:\home\iwsttty> [console]::OutputEncoding.EncodingName
日本語 (シフト JIS)

結局、STDOUTの外部エンコーディングが初期状態でnilなのがダメなんだろうな。せっかくEncoding.default_externalが適切に設定されているのに。

PowerShellでプロクシ設定

ぐぐっても意外と情報がなかったのでメモ。

> Set-Item -path Env:http_proxy -value http://proxy.youroffice.co.jp:7777
> $Env:http_proxy
http://proxy.youroffice.co.jp:7777
> gem install nokogiri
Fetching: nokogiri-1.5.5-x86-mingw32.gem (100%)
Successfully installed nokogiri-1.5.5-x86-mingw32
1 gem installed

DVDラベル印刷するときの見栄えの良いフォント

個人的な好みだけど。

タイトル(中)は、「Meiryo UI」サイズ18。下の図だと「CentOS release 6.2」のところ。
タイトル(小)は、「Courier New」サイズ11。「x86_64 Disc1」のところ。