本記事は、OpenStack Advent Calendar 2015 の12月3日の記事である。
はじめに
先日、OpenStack 環境を Juno から Liberty に一気にアップグレードしてみた。
なんでそんなことしたのかっていうと、先日のOpenStack Summit TokyoでPayPalがFolsomからKiloまでアップグレードしたっていうセッション見たら、自分にもできるって思ったからだ。
いや、結果、アップグレードできたんだけどさ。
まぁそんなわけで、見事に人柱になれたと思うので、ここではその時のことを記すことにした。
なお、そんなPayPal様のセッションはこちら。
PayPal's Cloud Journey From Folsom to Kilo -- What We Learned in the Upgrade
アップグレード前の構成
Component | Type |
---|---|
OS | Ubuntu 14.04 |
OpenStack | Juno |
Hypervisor | KVM |
Neutron Driver | VLAN + OVS (L3 Agentは動いてない) |
Cinder | 知らん |
アップグレード
Keystone -> Glance -> Nova -> Neutronの順番でやるのが一般的なんじゃないだろうか。Design Summitでは、各コンポーネントでバージョンがバラバラだって言う企業もいたから、順番は参考程度なんだが、全コンポーネントをアップグレードするならこの順番がいいと思う。
ちなみに、無停止アップグレードは絶対無理だからアキラメロン。って、Design Summitに居たハッカーのおっちゃんたちが言っとりました。
おっしゃる通りでございます。
. . . 閑話休題 . . .
ロードバランサの紐付け解除
Keystoneのフロントにあるロードバランサの5000番ポートに対する紐付けを解除し、完全に外部からの通信を止める。35357番ポートはそのままで問題ないと思われる。
Libertyリポジトリ導入
まずは、全てのサーバにLibertyリリースのリポジトリを入れよう。
$ sudo echo "deb http://ubuntu-cloud.archive.canonical.com/ubuntu trusty-updates/liberty main" > /etc/apt/sources.list.d/cloudarchive-liberty.list
$ sudo apt-get update
Keystone前準備
KeystoneのDBに溜まっているtokenを削除する。LibertyからはデフォルトでMemcachedをtoken用途で利用するようになっているので、そちらを使うことにした。つまり、事前にMemcached用サーバを用意する必要がある。
$ sudo su -s /bin/sh -c "keystone-manage token_flush" keystone
DBのTokenを綺麗に消したら次は定期実行していたCronを停止する。
$ sudo crontab -e -u keystone
以下の行を削除する。
@hourly /usr/bin/keystone-manage token_flush > /var/log/keystone/keystone-tokenflush.log 2>&1
潔く、Keystoneを止めましょう。
$ sudo service keystone stop
データベースのバックアップ
DBサーバにログインして以下のコマンドでDBのバックアップを取得する。
$ sudo mysqldump -uroot -p --opt --add-drop-database --single-transaction --master-data=2 keystone > liberty-keytone-db-backup.sql
$ sudo mysqldump -uroot -p --opt --add-drop-database --single-transaction --master-data=2 glance > liberty-glance-db-backup.sql
$ sudo mysqldump -uroot -p --opt --add-drop-database --single-transaction --master-data=2 nova > liberty-nova-db-backup.sql
$ sudo mysqldump -uroot -p --opt --add-drop-database --single-transaction --master-data=2 neutron > liberty-neutron-db-backup.sql
Keystoneアップグレード
ここでKeystoneのアップグレードを行う。いろんなパラメータが新規に追加されたり廃止予定になったりしているが、それについては書ききれないのでConfiguration Referenceを参照してほしい。アップグレード自体は、apt-get install XXXX
して、その後、keystone.conf
を書き換えて、プロセスを起動するだけである。基本的にはインストール手順のとおりに行えば問題ない(はず)。
ちなみに、conf
にDEBUGオプションを入れていると、プロセス起動直後のログに、conf
の各項目に何の値が設定されているのかが全て出力される。親切なことに、廃止予定の項目も指摘してくれる。これは全コンポーネント共通の仕組みなので、覚えておいて損はない。そういえば、Novaのusername
という項目で、username
は廃止予定だから使うのやめてuser-name
を使えってログに出ていたから、使うのやめてuser-name
使ったらエラー吐きまくってNovaが動かなくなった。ツンデレか。
話が逸れた。
keystone.conf
を配置したら以下のコマンドでLibertyリリースまでDBのスキーマのマイグレーションを行う。Kilo以降、KeystoneはApacheで稼働するようになったので、事前にKeystoneプロセスが止まっていることを確認してApacheを起動しておこう。
$ sudo service keystone stop
$ sudo service apache2 restart
$ sudo su -s /bin/sh -c "keystone-manage db_sync" keystone
エラーが出なければKeystoneのアップグレードは完了である。
一応、openstackコマンドが実行可能かどうかを確認しておこう。
$ ADMIN_TOKEN=${keystone.confに記載されているadmin_tokenの文字列}
$ export OS_TOKEN=${ADMIN_TOKEN}
$ export OS_URL=http://${Keystoneが稼働しているどれかのサーバのIPアドレス}:35357/v3
$ export OS_IDENTITY_API_VERSION=3
$ openstack service list
Glanceアップグレード
Keystone同様に_Configuration Reference_や公式のインストールガイドを確認しながら、環境にあったglance-api.conf
、glance-registry.conf
を配置しよう。
その後、Glanceの_db sync_を行う。
$ sudo service glance-api restart
$ sudo service glance-registry restart
$ sudo su -s /bin/sh -c "glance-manage db_sync" glance
エラーが出なければGlanceのアップグレードは完了である。
nova-apiアップグレード(Nova db sync 失敗編)
最大の難関である。NovaのDBを上手くLibertyまでマイグレーション出来ればアップグレードできたも同然である。がしかし、現実はそうそう上手くはいかなかった。
なので、この項ではアップグレードが上手くいかなかった時の状況をそのまま記すことにした。
まずは一気にLibertyまでアップグレードしてみる
まず、nova-api
が稼働しているサーバ上で、nova-api
をLibertyまでアップグレードした。
$ sudo apt-get install nova-api python-novaclient
その後_db sync_を行って、問題なければそのまま他のコンポーネントもアップグレードして完了となる(はずだった)。
で、以下のコマンドを叩くのだが、
$ sudo su -s /bin/sh -c "nova-manage db sync" nova
エラー発生:(
奇妙なnova-manageのオプション
エラーの内容としては、
-
db sync する前に
nova-manage db migrate_flavor_data
というコマンドを実行しろ
である。
が、実はLibertyリリースのnova-manage
コマンドにはこのオプションが入ってないのである。 ソースコードまで読んで調べたが本当になかった。んなアホなと思ったんだが、この時点でDBのスキーマ自体はかなり中途半端に変更されてしまっていた。
この状態で以下のことを行った。
- DBをドロップ(1度目)してリストア
- リポジトリをJunoリリースまで戻してパッケージを再インストール
次に疑ったのが、Kiloリリースである。なので、以下のことを行った。
- Kiloリリースの
nova-api
インストール -
nova-manage db migrate_flavor_data
実行 -
nova-manage db sync
実行
Kiloリリースのリポジトリを追加してインストールしたnova-api
にはnova-manage
のオプションにmigrate_flavor_data
があった。キタコレ!と思って、さっそくnova-manage db migrate_flavor_data
を実行した。そしたら成功したもんだから、そのまま一旦Kiloリリースまで上げてからLibertyリリースにしようと思って、Kiloリリース時点まで_db sync_した。
そしたら、またエラーが出た:(
flavor のテーブルのスキーマがおかしいという旨のエラーだった。嘘だろって思ったがマジだった。エラーの内容と状況を整理すると、Kiloリリースの_db sync_の後で、かつLibertyリリースの_db sync_の前に、nova-manage db migrate_flavor_data
を実行しろ、ということである。
つまり、正解はこうだ。
- Kiloリリースの
nova-api
インストール -
nova-manage db sync
実行 -
nova-manage db migrate_flavor_data
実行 - Libertyリリースの
nova-api
インストール -
nova-manage db sync
実行
apt 壊れた
この時点でまたもやDBのスキーマは中途半端に変更されてしまっていたため、Junoリリース時点まで戻して、またDBのドロップ(2度目)とリストアを行った。その後、Junoリリース時点のnova-api
に戻そうとしたのだが、今度は_apt_が壊れた....。
JunoとKiloとLibertyを何度も行き来していたせいで、依存パッケージがおかしなことになりインストールも削除もできなくなってしまった。しょうがないので、dpkg
コマンドで全3バージョン(Juno, Kilo, Liberty)のNovaに依存が貼られている依存パッケージ全てを削除した。そしたら_apt_が動くようになったので、またJunoリリースまで戻して再チャレンジすることとなる。
実はこれやってる最中に、この前テレビでやっていた「学校へ行こう!」の軟式globeの曲が脳内でループしていた。
「アホだなぁー」「そうだよアホだよ」
名曲である。
. . . 閑話休題 . . .
nova-apiアップグレード(Nova db sync 成功編)
ここまでを踏まえて、成功した手順を記す。
Kiloリリースをインストールする。
$ sudo echo "deb http://ubuntu-cloud.archive.canonical.com/ubuntu trusty-updates/kilo main" > /etc/apt/sources.list.d/cloudarchive-kilo.list
$ sudo apt-get update
$ sudo apt-get install nova-api python-novaclient
Kiloリリース時点のスキーマまでマイグレーションする。
$ sudo service nova-api restart
$ sudo su -s /bin/sh -c "nova-manage db sync" nova
$ sudo su -s /bin/sh -c "nova-manage db migrate_flavor_data" nova
その後、Libertyリリースに上げる。
$ sudo echo "deb http://ubuntu-cloud.archive.canonical.com/ubuntu trusty-updates/liberty main" > /etc/apt/sources.list.d/cloudarchive-liberty.list
$ sudo apt-get update
$ sudo apt-get install nova-api python-novaclient
Libertyリリース時点のスキーマまでマイグレーションする。
$ sudo service nova-api restart
$ sudo su -s /bin/sh -c "nova-manage db sync" nova
これで、nova-api
のアップグレードとNovaのDBのマイグレーションが完了となる。
ちなみに、このDBマイグレーション手順については、実はKiloのリリースノートに1行だけ記載があった。が、それでもあまり親切な書き方ではないから、事前に確認していてもハマってたと思う。
neutron-serverアップグレード
nova-api
がアップグレードできたら、次にneutron-server
をアップグレードする。
Keystone同様に_Configuration Reference_や公式のインストールガイドを確認しながら環境にあったneutron.conf
や、その他諸々を配置しよう。なお、Open vSwitchは_ml2_の設定ファイルが分割されているので注意が必要である。
その後、Neutronの_db sync_を行う。
$ sudo service neutron-server restart
$ sudo su -s /bin/sh -c "neutron-db-manage --config-file /etc/neutron/neutron.conf --config-file /etc/neutron/plugins/ml2/ml2_conf.ini upgrade head" neutron
エラーが出なければNeutronのDBはLibertyリリース時点のスキーマまでマイグレーションされている。
Nova/Neutronの残りのパッケージのアップグレード
ここまで完了すれば後は残っているパッケージを全てアップグレードしよう。ここから先は、特に問題は起こらない(はず)である。何をどうやってアップグレードするのかについては各環境に依存するので、ここでは記さないこととする。
ロードバランサの紐付け
Keystoneのフロントにあるロードバランサの5000番ポートに対してリアルサーバを紐付ける。
Horizonアップグレード
Horizonは、ぶっちゃけると、他のコンポーネントとバージョンが食い違っていても全く問題ないので、OpenStackがリリースされるたびにアップグレードしても何も問題ない。
が、Libertyリリースに限って言うと、というか、Libertyリリースの_deb_パッケージに限って言うと、アップグレードが失敗する。
なぜうまくいかなかったのかについて詳しい事象は判明していない。とりあえず、起こった事象を書き記すことにした。申し訳ない。
deb パッケージの謎
自分は今回_deb_ パッケージが新規インストール時とアップグレード時で挙動が違うという事象に遭遇した。普通は(きっと)そんなことはない(はず)なのだが、LibertyリリースのHorizonの_deb_パッケージに限って言えば、挙動が違った。
Junoリリースで稼働していたHorizonのサーバで、apt-get upgrade
、もしくはapt-get install openstack-dashboard
でHorizonのバージョンを上げた場合、CSSが読み込まれず、Horizonが2000年あたりの何とも簡素なWebページのようなデザインになる。
Horizonもコンポーネントが増えてごちゃごちゃしてきたから、ここで一つ Simple is Best ってことでデザイン変更したのかなって一瞬だけ思ったが、そんなことは全く無かった。いくらなんでも時代に逆行しすぎである。
. . . 閑話休題 . . .
ひとまずアップグレードプロセスに問題があるのだろうと踏んだので、調べるためにLibertyリリースのdeb-src
行をリポジトリに追記して、openstack-dashboardの_deb_パッケージを落としてくる。
$ sudo echo "deb-src http://ubuntu-cloud.archive.canonical.com/ubuntu trusty-updates/liberty main" >> /etc/apt/sources.list.d/cloudarchive-liberty.list
$ sudo apt-get update
$ sudo apt-get source openstack-dashboard
$ tar zxvf horizon_8.0.0-0ubuntu2~cloud0.debian.tar.gz
$ cd debian
README.source
を開くと早速以下の様なことが書いてあった。
During the Juno/14.10 development cycle, use of xstatic packages was introduced
so that CSS, JS and other static assets did not have to be embedded in the
horizon source tree.
怪しい、すごく怪しい。
ついでに、Icehouseリリースに遡ってソースを落としてくると、今度はREADME.compression
というファイルに以下のことが書いてある。
Until this can be scripted and integrated into package build, updating the
pre-compressed static CSS and JS requires a some manual steps:
sudo apt-get install python-lesscpy python-openstack-auth python-compressor
quilt pop top
./debian/rules refresh-static-assets
ふむ。
rules
ファイルとかちゃんと読めば、きちんとした原因が判明するだろう。
が、僕はこの時点で完全に力尽きていた。
唯一分かっていることは、一度JunoリリースのHorizon(Django含む)のパッケージを全て削除してLibertyリリースのパッケージを入れなおせば、問題なく動くということである。なお、この事象は、JunoからKiloに上げた際には発生しない。JunoからLiberty、もしくはKiloからLibertyにアップグレードした場合にのみ発生するのである。
というわけで、詳細知ってる人がいたら教えて下さい、お願いします;)
あと、Canonicalの中の人はこの_deb_パッケージどうにかして下さい:-(
アップグレードの終わり
ここまででOpenStack環境のアップグレードが完了となる。当然だが、こんな作業をマニュアルでやっていたら、いつか__腱鞘炎__になる。それが嫌なら、ChefとかPuppetとかAnsibleとかそういうの使ってやったほうがいい。たぶん。
お疲れ様でした:-)
その後の話
RFC3442(クラスレス静的ルート)問題
これはneutron-dhcp-agent
のお話である。皆さん、RFC3442をご存知だろうか?僕は最近まで知りませんでした(すいません)。基本的にアドレスやルートの配布はDHCPに頼ることとなる。そこで、DHCPの仕様を拡張し、IPアドレスをアサインする際に静的ルートテーブルも同時に配布する仕組みが導入された。それが、『クラスレス静的ルート』(RFC3442)である。
Junoリリース時点でRFC3442実装に対する対応はされていたそうだが、どうやらきちんと動いていなかったようだ。それがKiloリリース時点でバグ改修されており、見事Libertyリリースではちゃんと動くようになったのである。
何がどう問題だったのかというと、この実装がキチンと動いたことにより、使用していたExternal Networkのサブネットの組み方が間違っていたことが判明したのである。どう間違っていたのかを記すことは止めておくが、一つ言えるのはとにかく大変だったということである。
サブネットについてはネットワークのCIDRを変更すると事象が治るということが分かったのだが、Neutron APIからCIDRの変更が出来ないということも判明した。仕方ないので、Neutronデータベースのsubnetsテーブルに対して直接UPDATE文を実行する、という最終手段を取ることで解決した(おい)。
アップグレード作業から得た教訓
事前の検証は絶対必要
本番環境でのアップグレードを実施するまでに、本番と同等構成による新規でLiberty構築を1回と、事前にJunoの同等構成の検証環境を作っておき、そこからのアップグレードテストを1回やった。ちなみに今回の話は検証環境で起こった話です。おかげで本番環境ではすんなりアップグレードが出来ました。
どうしてもアップグレードが必要というわけでないのならやらないに越したことはない
OpenStackの各バージョンはリリースから約14ヶ月でEOLを迎える。つまり、リリースから約14ヶ月はBugfixがバックポートされるので、それでお茶を濁すというのもあり。もしくは、リリースタイイングごとにKeystoneだけアップグレードして、その他のコンポーネントはRegionをきちんと定義して小さくOpenStackクラスターを作っていくという手もある。当然、構築したOpenStackクラスターそのもののEOLを定義していく必要がある。
問題はアップグレード後にも起こる
アップグレード後に初めて気付く問題もたくさんある。事前にLaunchpadからバグやKnown Issueは確認して、アップグレードをするかどうかの判断基準にするとよい。とはいえ、リリース直後にアップグレードするとなると、それはただの人柱であり、バグがそもそもLaunchpadに登録されてない、なんてこともある。
最後に
ちなみに今日は僕の誕生日。