OpenStack環境で特定の仮想マシンが疎通できない場合のトラブルシュート

このエントリーは、エキサイト Advent Calendar 2016 の12/19の記事です。
エキサイトとしては初のAdvent Calendar参戦です。

いよいよクリスマスも今週末に迫りましたね。
みなさま、ケンタ●キーのご予約はお済みでしょうか?(笑)

先日、香港人の友達に「日本人はクリスマスにK●Cでチキン買うって本当か?」と聞かれ、「みんな予約して買ってるよ」と言ったら、「せっかくのクリスマスに、なんでこぞってそんな安いジャンクフード買うんだ?」と驚かれました。いつからターキーケ●タッキーになっちゃったのか、誰かご存知でしたら何卒ご教示下さい。

エキサイトのインフラエンジニアの福田です。

今年10月、スペインのバルセロナで行われたOpenStack 2016 Barcelonaに参加させて頂きました。
日々仕事を一緒にしている部署内の皆様におかれましては、
「パエリアは東京でも食えるだろ!」 とか
「サグラダ・ファミリア見に行きたいだけだろ!」 とか
内心思いつつも、快く見送って頂き、貴重な経験をさせて頂いたことに、この場を借りて御礼申し上げます。

さて、開催期間中、朝から晩まで様々なセッションに参加させていただきましたが、やはり皆様OpenStackに日々ご苦労されてるからか、トラブルシュート系のセッションは人気だったように思います。
そもそも、OpenStackで苦労してない人に会ったことないですね。はい。

そんなわけで、OpenStackを構築・運用する上で、トラブルシュートは避けて通れない道ですが、トラブルの内容によっては、いくらログを追っても解決の糸口が見えず、思わずちゃぶ台をひっくり返したくなる衝動にかられることも多々あるかと思います。
(※弊社オフィスの机がちゃぶ台なわけではありません)

そうなると、「じゃあ、次どこ見たらええねん!」と関東生まれ・関東育ち・関東在住にも関わらず、思わず関西弁で突っ込みたくなる衝動に駆らるわけです。
(※ご賛同頂けなくても差し支えございません)

そこで、今回は私が参加したセッションの中から、立ち見&座り見するほど盛況だった、RedHat社のエンジニアJon Jozwiak氏、Vinny Valdez氏による
"OpenStack Troubleshooting So Simple Even Your Kids Can Do It"
というセッションで紹介されていた、トラブルシュートの実例の1つを参考に、弊社のOpenStack環境で実際に検証した内容をご紹介します。
f0364156_11351026.jpg

弊社の構成はコントローラ3台+コンピュートノード複数台という構成です。

Nova以外のコンポーネントはすべてコントローラノードで動いていますので、その前提で話を進めます。
各コマンドを、「どのユーザー」で「どこで」実行するのか少々ややこしいので、先に定義いたします。
コントローラノードで実行  xxxx@controller
コンピュートノードで実行  xxxx@compute
クライアントPCで実行 xxxx@client
※今回はUbuntuDesktopがインストールされたPCですが、OpenStackのコマンドが叩ければどこでもOK

OpenStack上でのアカウントは2つ
 一般ユーザー  demo@xxxx
 管理者     admin@xxxx
OS上のアカウントはroot不要のコマンドもありますが、説明簡略化のため全てrootで作業します。
 管理者     root@xxxx
上記を組み合わせて表記します。

ターミナルエミュレータの画面を4つ開き、
demo@client
admin@client
root@controller
root@compute ※どこのcompute nodeかは後述
とご用意頂くと幸せになれそうです。

今回検証に用いたクライアントのバージョンは下記の通りです。バージョンが違う場合、特にメジャーバージョンが違う場合は意図した戻り値にならない場合が有りますので、ご了承ください。

さて、トラブルのお題は「Instance Connectivity」です。
何かしらの原因で仮想マシンへの疎通が取れない。という想定で、あくまで仮想マシン自体は正常に起動しているのが前提になります。

まず、そもそもネットワーク周りがどんな仕組みになっているのか?を理解する必要があるのは言うまでもありません。コンピュートホスト上に起動した仮想マシンから、実際に物理インターフェイスにたどり着くまで、下記のような流れとなります。
      • Tap interface
      • Linux bridge
      • Veth pair
      • OVS integration bridge
      • OVS patch ports
      • OVS provider bridge
      • Physical interface
ですので、これらの情報を調べる所から始めます。

まず、調査対象のインスタンスIDを取得します。
(ここでは調査対象のホスト名を「test-instance-2016」としています。)
[demo@client]~$ openstack server list --column ID --column Name --format value | grep test-instance-2016
5e007107-ca0f-452c-8217-48658279dbef test-instance-2016

これで、インスタンスIDがわかりましたので、今度はどこのコンピュートホストで動いているかを確認します。
[admin@client]~$ openstack server show 5e007107-ca0f-452c-8217-48658279dbef --column name --column OS-EXT-SRV-ATTR:host
+----------------------+--------------------------+
| Field | Value |
+----------------------+--------------------------+
| OS-EXT-SRV-ATTR:host | compute03 |
| name | test-instance-2016 |
+----------------------+--------------------------+
どこのcompute nodeで動いているかを示す「OS-EXT-SRV-ATTR:host」というカラムは、管理者権限でのみ参照可能ですので、ご注意ください。

これで、compute03 というコンピュートノードで動いていることがわかりました。
もちろんGUIで確認してもOKです。

次に、取得したインスタンスIDからポートのIDを取得します。
[demo@client]~$ neutron port-list --device_id 5e007107-ca0f-452c-8217-48658279dbef --fields id --format value
6047ba5c-9e0f-4d49-ac10-ae35957a24e3

先ほど確認したコンピュートノードにログインし、上記の戻り値の最初の8文字を使って、この仮想マシンに関連するポートの情報を取得します。
[root@compute] ip a | egrep "tap|qbr|qvb|qvo" | grep 6047ba5c
117: qbr6047ba5c-9e: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1400 qdisc noqueue state UP
118: qvo6047ba5c-9e@qvb6047ba5c-9e: <BROADCAST,MULTICAST,PROMISC,UP,LOWER_UP> mtu 1400 qdisc pfifo_fast master ovs-system state UP qlen 1000
119: qvb6047ba5c-9e@qvo6047ba5c-9e: <BROADCAST,MULTICAST,PROMISC,UP,LOWER_UP> mtu 1400 qdisc pfifo_fast master qbr6047ba5c-9e state UP qlen 1000
120: tap6047ba5c-9e: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1400 qdisc pfifo_fast master qbr6047ba5c-9e state UNKNOWN qlen 500
各ポートの名称はそれぞれ
 Tap interface は、tap...
 Linux bridge  は、qbr...
 Veth pair   は、qvb... と qvo...
となります。

これらの情報とOVS Integration Bridge、OVS Provider Bridge、物理インターフェイスの情報ををまとめると下記のようになります。
Tap interfacetap6047ba5c-9e
Linux bridgeqbr6047ba5c-9e
Veth pairqvb6047ba5c-9e / qvo6047ba5c-9e
OVS integration bridgebr-int
OVS patch portsint-br-ex / phy-br-ex
OVS provider bridgebr-ex
Physical interfacebond0
これで調査に必要な下調べが完了。ではここから実際に調査を開始します。

まず最初に、DHCPで正常にIPアドレスが取れているかをチェックします。
当該コンピュートノードで上記のTap Interfaceを指定して、tcpdumpを実行した状態で、当該仮想マシンを再起動してみます。この際、Soft Rebootしてしまうと、一旦Tap Interface自体を作り直されてしまい、tcpdumpが終了してしまいますので、GUIのConsoleから"Send CtrlAltDel" が手っ取り早いかと思います。

下記のようにDHCPのRequest/Replyのやり取りが出力されていれば、IPアドレスの取得は問題ないかと思います。何も出力されない場合は、DHCP周りに何か問題がある可能性が高いです。
[root@compute] tcpdump -eni tap6047ba5c-9e port bootpc or port bootps

tcpdump: WARNING: tap6047ba5c-9e: no IPv4 address assigned
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on tap6047ba5c-9e, link-type EN10MB (Ethernet), capture size 65535 bytes
18:33:16.815223 fa:16:3e:86:2d:7f > fa:16:3e:27:05:29, ethertype IPv4 (0x0800), length 342: 10.114.0.68.bootpc > 10.114.0.20.bootps: BOOTP/DHCP, Request from fa:16:3e:86:2d:7f, length 300
18:33:24.194755 fa:16:3e:86:2d:7f > Broadcast, ethertype IPv4 (0x0800), length 342: 0.0.0.0.bootpc > 255.255.255.255.bootps: BOOTP/DHCP, Request from fa:16:3e:86:2d:7f, length 300
18:33:24.195924 fa:16:3e:27:05:29 > fa:16:3e:86:2d:7f, ethertype IPv4 (0x0800), length 372: 10.114.0.20.bootps > 10.114.0.68.bootpc: BOOTP/DHCP, Reply, length 330
18:33:24.196206 fa:16:3e:86:2d:7f > Broadcast, ethertype IPv4 (0x0800), length 342: 0.0.0.0.bootpc > 255.255.255.255.bootps: BOOTP/DHCP, Request from fa:16:3e:86:2d:7f, length 300
18:33:24.196211 fa:16:3e:12:ff:3d > fa:16:3e:86:2d:7f, ethertype IPv4 (0x0800), length 372: 10.114.0.21.bootps > 10.114.0.68.bootpc: BOOTP/DHCP, Reply, length 330
18:33:24.196271 fa:16:3e:24:83:a7 > fa:16:3e:86:2d:7f, ethertype IPv4 (0x0800), length 372: 10.114.0.22.bootps > 10.114.0.68.bootpc: BOOTP/DHCP, Reply, length 330
18:33:24.196971 fa:16:3e:27:05:29 > fa:16:3e:86:2d:7f, ethertype IPv4 (0x0800), length 390: 10.114.0.20.bootps > 10.114.0.68.bootpc: BOOTP/DHCP, Reply, length 348
このように、問題の仮想マシンに紐付けられてるTap Interfaceを直接指定してtcpdumpをすることで、他の仮想マシンのトラフィックに邪魔されること無く調査ができます。

もしDHCP周りがおかしいなと思った場合は、まずは下記点を確認します。
[admin@client]~$ neutron agent-list | grep DHCP
| 4e2e7d4d-f993-4191-8d79-a8d55fa5c21f | DHCP agent | controller01 | :-) | True | neutron-dhcp-agent |
| f44e4362-8017-4bc8-9666-e33ab2fe7556 | DHCP agent | controller03 | :-) | True | neutron-dhcp-agent |
| f55d3a06-b85f-4b08-9b30-c482e5e96381 | DHCP agent | controller02 | :-) | True | neutron-dhcp-agent |
ニッコリと笑顔が並んでればOKです。

次にdnsmasqプロセスが稼働しているか確認します。
[root@controller] ps aux | grep dnsmasq
nobody 2115 0.0 0.0 30692 1536 ? S 11月07 0:14 /usr/sbin/dnsmasq --no-resolv --keep-in-foreground --no-hosts --bind-interfaces --pid-file=/run/sendsigs.omit.d/network-manager.dnsmasq.pid --listen-address=127.0.1.1 --conf-file=/var/run/NetworkManager/dnsmasq.conf --cache-size=0 --proxy-dnssec --enable-dbus=org.freedesktop.NetworkManager.dnsmasq --conf-dir=/etc/NetworkManager/dnsmasq.d

どちらかもしくは両方とも意図した結果でない場合は、下記コマンドでneutron-dhcp-agentを再起動します。
[admin@client]~$ openstack-service restart neutron-dhcp-agent

その後、再度上記の確認を行います。
当然ながら、そもそもneutron-dhcp-agentが上がってなければにっちもさっちも行かないですし、そもそもすべての仮想マシンで疎通が取れないかと思います。

この辺の原因は基本的なセットアップの問題の可能性が高いので、ここでは割愛します。
今回はあくまで、「なんでオマエだけ疎通しないんだよっ!」っていう、こたつの上のみかんを窓に向かって投げたくなるシチュエーションです。
(※弊社オフィスの机がこたつなわけではありません)

Agentが正常であることがわかりましたので、さらに下記点を確認してみます。
仮想マシンのネットワークを調べます。(ここではsrv_networkという変数に入れておきます)
[demo@client]~$ srv_network=`openstack server show test-instance-2016 --column addresses --format value | cut -d= -f 1`

取得したネットワークの名前からIDを調べます。(同様にnet_id)
[demo@client]~$ net_id=`openstack network show $srv_network --column id --format value`

同様にサブネットのIDを調べます。(同様にsubnet)
[demo@client]~$ subnet=`openstack network show $srv_network --column subnets --format value`

同様に当該サブネットに関連するDHCPのポート一覧を取得します。(同様にport_list)
[demo@client]~$ port_list=(`openstack port list --device-owner network:dhcp --format value --column ID --column 'Fixed IP Addresses' | grep $subnet | awk '{print $1}'`)

それぞれのポートのnetwork_idが一致していることを確認します。
[demo@client]~$ for p in ${port_list[@]};do
openstack port show $p --column network_id --format value
done | sort | uniq
15548108-b67e-42c6-bc36-b076d0898c94
ここで複数のIDが出力される場合は一致していません。

コントローラノードで上記network_idからDHCP Agent IDを確認。
[root@controller] ip netns | grep 15548108-b67e-42c6-bc36-b076d0898c94
qdhcp-15548108-b67e-42c6-bc36-b076d0898c94

上記network_idの先頭に「qdhcp-」というプレフィックスが付与されたDHCP Agentがいることがわかります。

上記DHCP Agent IDはネームスペースになっているので、コントローラノードで上記IDを指定して、インターフェイス情報を確認します。
[root@controller] ip netns exec qdhcp-15548108-b67e-42c6-bc36-b076d0898c94 ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN
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
32: tap89fa4ae5-9f: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1400 qdisc noqueue state UNKNOWN
link/ether fa:16:3e:27:05:29 brd ff:ff:ff:ff:ff:ff
inet 10.114.0.20/24 brd 10.114.0.255 scope global tap89fa4ae5-9f
valid_lft forever preferred_lft forever
inet 169.254.169.254/16 brd 169.254.255.255 scope global tap89fa4ae5-9f
valid_lft forever preferred_lft forever
inet6 fe80::f816:3eff:fe27:529/64 scope link
valid_lft forever preferred_lft forever

上記のTap interfaceと前述のDHCP Agent IDを組み合わせて、再度tcpdumpを実行してDHCP関連の出力がされるか確認します。前回同様、GUIのConsoleから"Send CtrlAltDel" が手っ取り早いかと思います。
[root@controller] ip netns exec qdhcp-15548108-b67e-42c6-bc36-b076d0898c94 tcpdump -leni tap89fa4ae5-9f port bootpc or port bootps
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on tap89fa4ae5-9f, link-type EN10MB (Ethernet), capture size 65535 bytes
17:50:33.626407 fa:16:3e:86:2d:7f > fa:16:3e:12:ff:3d, ethertype IPv4 (0x0800), length 342: 10.114.0.68.bootpc > 10.114.0.21.bootps: BOOTP/DHCP, Request from fa:16:3e:86:2d:7f, length 300
17:50:41.204057 fa:16:3e:86:2d:7f > Broadcast, ethertype IPv4 (0x0800), length 342: 0.0.0.0.bootpc > 255.255.255.255.bootps: BOOTP/DHCP, Request from fa:16:3e:86:2d:7f, length 300
17:50:41.204840 fa:16:3e:27:05:29 > fa:16:3e:86:2d:7f, ethertype IPv4 (0x0800), length 372: 10.114.0.20.bootps > 10.114.0.68.bootpc: BOOTP/DHCP, Reply, length 330
17:50:41.205466 fa:16:3e:86:2d:7f > Broadcast, ethertype IPv4 (0x0800), length 342: 0.0.0.0.bootpc > 255.255.255.255.bootps: BOOTP/DHCP, Request from fa:16:3e:86:2d:7f, length 300
17:50:41.206198 fa:16:3e:27:05:29 > fa:16:3e:86:2d:7f, ethertype IPv4 (0x0800), length 390: 10.114.0.20.bootps > 10.114.0.68.bootpc: BOOTP/DHCP, Reply, length 348
今回想定している「仮想マシンは起動しているのに疎通できない」という場合、DHCP周りでうまくIPアドレスが取れないということが多いため、上記手順で切り分けができるかと思います。

長くなってしまったので、仮にDHCP周りに問題があると判断したあとの対処法に関しては、別の機会にご紹介できればと思います。

明日は、伊藤さんのAndroidTVの話です。 家にテレビないですけど楽しみです。

エンジニア募集
エキサイトではエンジニアとして一緒に働いてくださる方を新卒採用と中途採用で募集しています。
詳しくはこちら↓
をご覧ください。

[PR]
by ex-engineer | 2016-12-19 00:00