LXD 2.0でlive migrationしてみる

基本的には

に書いてある通りなんで、これの補足という形になります。


まず、live migrationを行う(ホストh1にあるコンテナc1をホストh2にlive migrationする場合)のにこのようなコマンドを実行すると

hirose31@h1$ lxc move c1 h2:

「error: Error transferring container data: websocket: bad handshake」というエラーで失敗し、以下のようにlocalの場合でもremote名を添えて実行すれば成功しました。

hirose31@h1$ lxc move h1:c1 h2:

従って、予め自ホストの分もremoteの登録が必要でした。

hirose31@h1$ lxc remote add h1 192.168.33.21
hirose31@h1$ lxc remote add h2 192.168.33.22

hirose31@h2$ lxc remote add h1 192.168.33.21
hirose31@h2$ lxc remote add h2 192.168.33.22

次にハマったのは、lxc moveするとこのようなエラーで失敗した点です。

hirose31@h1$ lxc move h1:c1 h2:
error: Error transferring container data: checkpoint failed:
(00.093894) Error (sockets.c:129): Diag module missing (-2)
(00.173958) Error (sockets.c:339): Sockets (family 16, proto 0) are not collected
(00.173977) Error (cr-dump.c:1303): Dump files (pid: 8476) failed with -1
(00.178203) Error (cr-dump.c:1600): Dumping FAILED.

エラーメッセージを見て、てっきりエラーの原因を表示するためのGo(LXDのlxcコマンドはGoで書かれています)のソケット関連のdiagnostic(診断)ライブラリが足りてないのかな?と思い、いろいろ調べたのですが、結局原因はGoは関係なく、netlink_diag.koというkernel moduleがなかったためでした。思い込みはよくないですね!

Ubuntu 16.04ではnetlink_diag.koは別パッケージに収録されているのでそれをインストールします。

sudo apt-get install linux-image-extra-$(uname -r)

これでlive migrationが成功しました!

ちなみにですけど、netlink_diag.koがないとstateful stop/start やstateful snapshotsも失敗します。


他に気をつける点は、ググるとひっかかる古い情報ではprofileを変更してたりしますが、デフォルトのdefault profileだけで動きました。むしろ、下手にいじると動かない場合がありました。

LXDの感想

システムコンテナとしてDockerを使い、CentOS 6とUbuntu 14.04を動かそうとしたときはいろいろハマった(gettyがCPU食いつぶすとかudevadm settleでbootが止まるとかとか)んですが、LXDでは images.linuxcontainers.org のイメージを使った限りではすんなりCentOS 6やUbuntu 16.04が動かせていまのところはだいぶ好印象です。

LXDはOpenStackとの連携もできるようですし、より軽量なKVMの代替としてよい選択肢になるんじゃないかと思います。

追記 2017-02-10

live migration元と先とでlxdのバージョンが違うと(例えば2.0.5と2.0.8)このようなエラーで失敗する。

move: error: Bad key: volatile.idmap.base

バージョンを揃えれば成功する。apt-get install lxd でコンテナは稼働したままバージョンを上げられた。

www.twitch.tvの名前が引けない、もしくはチェックサムが0x0000のUDPパケットの件のまとめ

先に原因を書いておくと、
チェックサムが 0x0000 のUDPパケットが戻ってくると、自分の環境では「どこか」で再計算された誤ったチェックサムが付与され、チェックサムが合わないのでユーザーランドに届く前に破棄されていました。



以下、詳しく。

ことの始まりは、自宅でtwitchでSplatoon(先日ようやくSになりました!)の動画配信を見ようとしたのですがアクセスできないのに気づいたことでした。

ブラウザに表示されるエラーメッセージからして名前が引けないようなので、digで試してみたら引けませんでした。(10.6.25.2は宅内のキャッシュサーバー(djbdns))

$ dig www.twitch.tv @10.6.25.2
(しばらくだんまり)
; <<>> DiG 9.9.5-3ubuntu0.7-Ubuntu <<>> www.twitch.tv @10.6.25.2
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: SERVFAIL, id: 37843
;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 0

;; QUESTION SECTION:
;www.twitch.tv.                 IN      A

;; Query time: 54 msec
;; SERVER: 10.6.25.2#53(10.6.25.2)
;; WHEN: Wed Jan 27 12:13:35 JST 2016
;; MSG SIZE  rcvd: 31

引ける環境でtwitch.tvのネームサーバーを調べて問い合わせてみても引けない。

### 引ける環境
$ dig -t ns twitch.tv

; <<>> DiG 9.9.5-3ubuntu0.7-Ubuntu <<>> -t ns twitch.tv
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 5068
;; flags: qr rd ra; QUERY: 1, ANSWER: 6, AUTHORITY: 0, ADDITIONAL: 12

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4000
;; QUESTION SECTION:
;twitch.tv.                     IN      NS

;; ANSWER SECTION:
twitch.tv.              10781   IN      NS      ns1.p18.dynect.net.
twitch.tv.              10781   IN      NS      a3.verisigndns.com.
twitch.tv.              10781   IN      NS      a2.verisigndns.com.
twitch.tv.              10781   IN      NS      ns3.p18.dynect.net.
twitch.tv.              10781   IN      NS      ns2.p18.dynect.net.
twitch.tv.              10781   IN      NS      a1.verisigndns.com.

;; ADDITIONAL SECTION:
ns1.p18.dynect.net.     9110    IN      A       208.78.70.18
ns1.p18.dynect.net.     281     IN      AAAA    2001:500:90:1::18
a3.verisigndns.com.     3571    IN      A       69.36.145.33
a3.verisigndns.com.     1940    IN      AAAA    2001:502:cbe4::33
a2.verisigndns.com.     3573    IN      A       209.112.114.33
a2.verisigndns.com.     3581    IN      AAAA    2620:74:19::33
ns3.p18.dynect.net.     10186   IN      A       208.78.71.18
ns3.p18.dynect.net.     281     IN      AAAA    2001:500:94:1::18
ns2.p18.dynect.net.     17914   IN      A       204.13.250.18
a1.verisigndns.com.     2571    IN      A       209.112.113.33
a1.verisigndns.com.     3499    IN      AAAA    2001:500:7967::2:33

;; Query time: 1 msec
;; SERVER: 172.24.64.100#53(172.24.64.100)
;; WHEN: Wed Jan 27 12:31:30 JST 2016
;; MSG SIZE  rcvd: 408
$ dig www.twitch.tv @209.112.113.33
(しばらくだんまり)
; <<>> DiG 9.9.5-3ubuntu0.7-Ubuntu <<>> www.twitch.tv @209.112.113.33
;; global options: +cmd
;; connection timed out; no servers could be reached

ぬーんと思っていたらアドバイスもらったので、

tcpdumpしながらstraceでdigを実行してみる。

# tcpdump -nlSxX -i any host 209.112.113.33
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on any, link-type LINUX_SLL (Linux cooked), capture size 65535 bytes
12:51:30.078313 IP 10.6.25.39.39302 > 209.112.113.33.53: 58570+ [1au] A? www.twi
tch.tv. (42)
        0x0000:  4500 0046 77c5 0000 4011 9d23 0a06 1927  E..Fw...@..#...'
        0x0010:  d170 7121 9986 0035 0032 be51 e4ca 0120  .pq!...5.2.Q....
        0x0020:  0001 0000 0000 0001 0377 7777 0674 7769  .........www.twi
        0x0030:  7463 6802 7476 0000 0100 0100 0029 1000  tch.tv.......)..
        0x0040:  0000 0000 0000                           ......
12:51:30.090812 IP 209.112.113.33.53 > 10.6.25.39.39302: 58570*- 1/0/1 CNAME ssl
.cdn.twitch.tv.c.footprint.net. (89)
        0x0000:  4500 0075 01ca 0000 3711 1bf0 d170 7121  E..u....7....pq!
        0x0010:  0a06 1927 0035 9986 0061 f393 e4ca 8500  ...'.5...a......
        0x0020:  0001 0001 0000 0001 0377 7777 0674 7769  .........www.twi
        0x0030:  7463 6802 7476 0000 0100 01c0 0c00 0500  tch.tv..........
        0x0040:  0100 0000 0f00 2303 7373 6c03 6364 6e06  ......#.ssl.cdn.
        0x0050:  7477 6974 6368 0274 7601 6309 666f 6f74  twitch.tv.c.foot
        0x0060:  7072 696e 7403 6e65 7400 0000 2910 0000  print.net...)...
        0x0070:  0000 0000 00                             .....
...
$ strace -f -s 100 dig www.twitch.tv @209.112.113.33
[pid  1162] sendmsg(20, {msg_name(16)={sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("209.112.113.33")}, msg_iov(1)=[{"Oh\1 \0\1\0\0\0\0\0\1\3www\6twitch\2tv\0\0\1\0\1\0\0)\20\0\0\0\0\0\0\0", 42}], msg_controllen=0, msg_flags=0}, 0 <unfinished ...>
...
[pid  1162] recvmsg(20, 0x7fb7f1ca5c90, 0) = -1 EAGAIN (Resource temporarily unavailable)
...
[pid  1162] sendmsg(20, {msg_name(16)={sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("209.112.113.33")}, msg_iov(1)=[{"Oh\1 \0\1\0\0\0\0\0\1\3www\6twitch\2tv\0\0\1\0\1\0\0)\20\0\0\0\0\0\0\0", 42}], msg_controllen=0, msg_flags=0}, 0) = 42
...
[pid  1162] recvmsg(20, 0x7fb7f1ca5c90, 0) = -1 EAGAIN (Resource temporarily unavailable)
...

なんと、ユーザーランド(digコマンド)には応答が届いてないけど、kernelにはUDPパケットが届いてるご様子。

うーんなんでユーザーランドに届かないんだろう。。。と思っていたらまたアドバイスが。

確かに、引ける環境ではチェックサムが 0x0000 になっているけど、引けない環境では 0xf393 になっている。

### 引ける環境
04:09:08.940710 IP 209.112.113.33.53 > 10.0.0.211.35082: 12580*- 1/0/1 CNAME ssl.cdn.twitch.tv.c.footprint.net. (89)
        0x0000:  4500 0075 bc5c 0000 3a11 76b7 d170 7121  E..u.\..:.v..pq!
        0x0010:  0a00 00d3 0035 890a 0061 0000 3124 8500  .....5...a..1$..
        0x0020:  0001 0001 0000 0001 0377 7777 0674 7769  .........www.twi
        0x0030:  7463 6802 7476 0000 0100 01c0 0c00 0500  tch.tv..........
        0x0040:  0100 0000 0f00 2303 7373 6c03 6364 6e06  ......#.ssl.cdn.
        0x0050:  7477 6974 6368 0274 7601 6309 666f 6f74  twitch.tv.c.foot
        0x0060:  7072 696e 7403 6e65 7400 0000 2910 0000  print.net...)...
        0x0070:  0000 0000 00                             .....

### 引けない環境
12:51:30.090812 IP 209.112.113.33.53 > 10.6.25.39.39302: 58570*- 1/0/1 CNAME ssl
.cdn.twitch.tv.c.footprint.net. (89)
        0x0000:  4500 0075 01ca 0000 3711 1bf0 d170 7121  E..u....7....pq!
        0x0010:  0a06 1927 0035 9986 0061 f393 e4ca 8500  ...'.5...a......
        0x0020:  0001 0001 0000 0001 0377 7777 0674 7769  .........www.twi
        0x0030:  7463 6802 7476 0000 0100 01c0 0c00 0500  tch.tv..........
        0x0040:  0100 0000 0f00 2303 7373 6c03 6364 6e06  ......#.ssl.cdn.
        0x0050:  7477 6974 6368 0274 7601 6309 666f 6f74  twitch.tv.c.foot
        0x0060:  7072 696e 7403 6e65 7400 0000 2910 0000  print.net...)...
        0x0070:  0000 0000 00                             .....

引けない環境のを Wireshark でみてみると、incorrectと表示されています。

0x0000 のチェックサムは、(それをパブリックなネットワークで使うかはさておき)仕様的には妥当な値ではあります。

An all zero transmitted checksum value means that the transmitter generated no checksum (for debugging or for higher level protocols that don't care).

https://tools.ietf.org/html/rfc768

余談ですが、チェックサムについてこれがおもしろかったです。


というわけで、恐らく、自分の環境の「どこか」でUDPチェックサムの再計算をしていて、その計算ロジックが間違っているんじゃないかと思っています。

「どこか」はNATしている民生用のBUFFALOのブロードバンドルーターが怪しいんじゃないかと思っていて問い合わせ中です。続報があればここに書き足します。

補足

0x0000なチェックサムUDPパケットを返すのは a1.verisigndns.com [209.112.113.33] だけではなく、

も0x0000なチェックサムを返していました。(2016-01-27現在)


twitch.tvドメインのネームサーバーは ns1.p18.dynect.net 等はチェックサムつきのパケットを返すので、当座は

echo 208.78.70.18 > dnscache/root/servers/twitch.tv

して凌ぐことにしました。。

続報

Verisign


ふろく

DHCPでIPアドレスとかは設定するけどresolv.confはいぢられたくない場合

DHCPIPアドレスとかは設定するけど /ec/resolv.conf は生成・変更されたくない場合のメモ。

Ubuntu

以下、14.04と15.04で確認。

手短に言うと、

  • /etc/resolv.conf をsymlinkではなく実ファイルにした上で、
  • 以下の内容で /etc/dhcp/dhclient-enter-hooks.d/disable-make_resolv_conf を作る
make_resolv_conf() { :; }

細かく書くと、こんな感じになってる。

  • dhclientが/sbin/dhclient-scriptを折りにふれ実行する
  • dhclient-script は resolv.conf を生成するためにシェル関数 make_resolv_conf を呼ぶ
  • dhclient-script にはフックポイントがあり、以下の場所にあるファイルを . (source) でロードしてくれる。つまり、ここで同名で シェル関数 make_resolv_conf を上書き定義すれば、resolv.conf 生成の処理を制御できる
    • /etc/dhcp/dhclient-enter-hooks
    • /etc/dhcp/dhclient-enter-hooks.d/*
    • /etc/dhcp/dhclient-exit-hooks
    • /etc/dhcp/dhclient-exit-hooks.d/*
    • ロードのタイミング (enter/exit) の説明は割愛
    • ディレクトリの方は、run-parts --list で列挙されたのがロードされるので、ファイル名の規則とかは run-parts(8) を参照
  • resolvconf パッケージがインストールされていると /etc/dhcp/dhclient-enter-hooks.d/resolvconf というファイルが存在するはず
  • /etc/dhcp/dhclient-enter-hooks.d/resolvconf は、デフォルトの make_resolv_conf を上書いて /sbin/resolvconf を使って /etc/resolv.conf を生成するようにしている
    • ただし、/etc/resolv.conf が symlink ではない場合は make_resolv_conf の上書きはしない

CentOS 6.5

NetworkManagerを無効化した上で、ifcfg-IF に PEERDNS=no を書く。

chkconfig NetworkManager off
service NetworkManager stop

vi /etc/sysconfig/network-scripts/ifcfg-eth0
# PEERDNS=no と書く

CentOSでもh2oでOCSP Staplingしたい

h2o/1.5.2です。

(たいていそうだと思いますが)中間CAから発行されたサーバー証明書の場合は、h2oではcertificate-fileで指定するファイルの内容を

  1. サーバー証明書
  2. 中間CAの証明書

という順序でcatで結合したものにします。順序があるので注意してください。参考: スマートフォン等、携帯端末の一部のみ「証明書が信頼できない」と警告が表示されます | Symantec

さて、h2oは起動時にOCSPレスポンダからレスポンスを得て、それのverifyが成功すればOCSP Staplingを有効にします。

具体的には、share/h2o/fetch-ocsp-response の中でopensslコマンドを使ってこの様にしています。

# certificate-fileで指定されたサーバー証明書からOCSPレスポンダのURIを得る
openssl x509 -in $certificate_file -noout -ocsp_uri

# certificate-fileから中間CAの証明書を得て($intermediate_file)、OCSPレスポンダにリクエストしてレスポンスを保存する
openssl ocsp -issuer $intermediate_file -cert $certificate_file -url $ocsp_uri -header Host $ocsp_host -noverify -respout resp.der

# OCSPレスポンスのverify。3つのうちどれかが通ればよい。
# 「for comodo」らしい
openssl ocsp -respin resp.der -VAfile $intermediate_file
# OpenSSL >= 1.0.2 で実装されたオプションを使用
openssl ocsp -respin resp.der -partial_chain -trusted_first -CAfile $intermediate_file
# OpenSSL <= 1.0.1 だとこれになる
openssl ocsp -respin resp.der -CAfile $intermediate_file

CentOS 6のOpenSSLは1.0.1なので、最後の3つめの方法でverifyすることになります。


さてさて、opensslはローカルにあるファイルからroot CA群の証明書(trust anchors)を得るのですが、オプションによって探す場所が変わります。

  • -CAfile が指定されていない場合
    • /etc/pki/tls/cert.pem (実体は /etc/pki/tls/certs/ca-bundle.crt で、多数の証明書が1ファイルに結合された内容)
    • /etc/pki/tls/certs/
  • -CAfile が指定されている場合


先ほどCentOS 6の場合は

openssl ocsp -respin resp.der -CAfile $intermediate_file

でverifyすると書きました。

resp.derのissuerは中間CAです。中間CAの証明書は-CAfileで指定しているのでパスします。
が、中間CA証明書のissuer (=root CA) の証明書は見つけることができないので、エラーとなってしまいます。これがCentOS 6でOCSP Staplingが有効にならない原因です。

ちなみに、-CAfileで指定するファイルの内容を、中間CA+root CAの証明書、にすればパスします。

ちなみにちなみに、Ubuntu 14.04では(パスが /usr/lib/ssl/certs/になりますが)root CA証明書へのhash linkがあるのでパスします。


で、CentOS 6でOCSP Staplingを有効にするにはどうすればいいか?なのですが、自分のサーバー証明書のroot CAの証明書を別途入手して、どこかに置いて /etc/pki/tls/certs/ の下にhash linkを作るのがいいのではないかと思います。

オマケ

hash linkを作るコマンドライン(ln)を吐く便利関数

cert_hashlink() {
  [ -r $1 ] || { echo "cannot read file: $1"; return 1; }
  echo "ln -snf $1 $(openssl x509 -noout -hash < $1).0"
}

h2oでmrubyを有効にしてビルドするにはruby >= 1.9が必要

h2o/1.5.2です。

h2oでmrubyを有効にするには、

cmake -DWITH_MRUBY=ON .

とすればよいのですが、ビルドを完遂するにはruby >= 1.9が必要です。

なぜなら、deps/mruby-onig-regexp/mrbgem.rake にこういう処理

    def run_command(env, command)
      STDOUT.sync = true
      Open3.popen2e(env, command) do |stdin, stdout, thread|
        print stdout.read
        fail "#{command} failed" if thread.value != 0
      end
    end

があるのですが、Open3#popen2e が使えるのは 1.9 からだからです。

マルチスレッドでgetaddrinfo(3)するとたまに Temporary failure in name resolution (EAI_AGAIN) で失敗する件

マルチスレッドでgetaddrinfo(3)するとたまにTemporary failure in name resolution (EAI_AGAIN) で失敗します。

自分は↓な環境で確認しました。

こういうコードで再現します。(ruby 2.1.4で確認)

なお、便宜的にrubyで再現コードを書いていますが、該当環境であれば言語問わずマルチスレッド+getaddrinfo(3)の組み合わせで発現する問題です。



対処法は、

rpmCentOS 6 Updatesにあるバージョン 2.12-1.149.el6_6.9 のに上げればOKです。


ちなみに自分は、Chef実行時にたまにberksのAPIサーバーの名前が引けなくてコケてたのがこれで直りました。

h2o試してみました、もしくはとりあえずサクッと既存のサイトをHTTP/2化する方法

先日、HTTP/2が正式な仕様として承認されると同時に、その実装であるH2Oのv1.0.0もリリースされました。

HTTP/2の情報はちょいちょいウォッチはしていたのですが、今までHTTP/2なサーバーを動かしたことはなく、いい機会なので自分のサイトをH2Oを使ってHTTP/2対応してみました。



大したことはやってないのですが、Apacheでサービスしており、認証やアクセス制限、ごにょごにょ黒魔術、CGI(!)やSSI(!!)なども動いているので、ApacheをH2Oにリプレースするのは無理でした。

そこで、H2Oをリバプーとして前段に置き、Apacheを後段に置く構成にしました。

設定ファイルはこんな感じです。

# -*- mode: yaml; -*-

user: www-data

max-connections: 1024
num-threads: 4

listen:
  host: 10.6.25.29
  port: 80
listen:
  host: 10.6.25.29
  port: 443
  ssl:
    certificate-file: /usr/irori/etc/ssl/cert_web/irori.org-cert.pem
    key-file: /usr/irori/etc/ssl/priv_web/irori.org-priv.pem
    cipher-suite: AES128-SHA:ECDHE-RSA-AES128-SHA256:AES128-GCM-SHA256:HIGH:MEDIUM:!aNULL:!MD5:!RC4

hosts:
  "*":
    access-log: /dev/stdout
    paths:
      /:
        proxy.reverse.url: http://10.6.25.29:9080
        proxy.preserve-host: ON

"www.irori.org"のグローバルIPアドレスの80と443宛てのはルーターで10.6.25.29にNATされるので、それをlistenします。

443の方は、SSLの設定をします。

証明書は StartSSL で無料で発行したもらったものです。

certificate-file には発行されたサーバー証明書を指定しますが、中間CAの証明書も必要な場合は、cat でくっつけたファイルを指定します。

くっつける際には、くっつける順序に注意してください。最初にサーバー証明書、つづいて中間CA証明書やクロスルート証明書という風に、信頼チェーンの順序にしましょう。


今回はまるっとApacheに投げたいので、hostsは1つだけです。

H2Oは、リクエストのHostヘッダに合致するものがなければ最初の項目の設定に従うので、便宜的に「"*"」として、そこでぜんぶひっかけてApacheに丸投げしています。


以上、HTTP/0.9ってのもあったよね〜〜と感慨にふけっている老害がお送りしました。