oVirt 4.3 HCI環境をoVirt 4.4へアップグレード（その１）

[SRA Advent Calendar](https://qiita.com/advent-calendar/2020/sra)の4日目です。
ネットワークシステムサービス第１事業部の ふじまき です。

[oVirt](https://ovirt.org/) CentOS Host(a.k.a. Thick Host)のHyperconverged InfrastructureのSelf-Hosted Engine環境を4.3から4.4へアップグレードする方法を紹介します。

公式のアップグレード手順は[こちら](https://www.ovirt.org/documentation/upgrade_guide/#SHE_Upgrading_from_4-3)ですが、これはFC/iSCSI接続の共有ディスクのある構成向けなので、Hyperconverged Infrastructure(以下、HCI)環境では使えません。
ということで公式なHCI環境向けアップグレード手順が無い中で試行錯誤して捻りだした非公式アップグレード手順を紹介したいと思います。

なお、商用版のRed Hat Hyperconverged Infrastructure for VirtualizationではThick Hostは[サポート外](https://access.redhat.com/documentation/ja-jp/red_hat_hyperconverged_infrastructure_for_virtualization/1.8/html/deploying_red_hat_hyperconverged_infrastructure_for_virtualization/rhhi-requirements)となっているので今後も公式の手順は出てこない可能性が高いです。（この構成止めた方がよいかも）

# 注意事項
+ 最悪の事態（クラスタ全滅、全データロスト）に備えてバックアップを取っておきましょう。:smiley:
+ 全ての手順が必須かどうかは不明です。（不要な作業が含まれている可能性あり）

# 用語
ここでは以下とします。

+ 物理ホスト：oVirtおよびGlusterノードを兼務する物理サーバ
+ EngineVM：Web管理コンソールを提供するoVirt管理用仮想マシン

# 前提条件
+ 全ての物理ホスト及びEngineVMはoVirt 4.3.10までアップグレード済である事
+ 全ての仮想マシンの互換性レベルが4.3になっている事（未適用は×）
+ 新しいEngineVMのデプロイ先となる新規Glusterボリュームを作るための未使用領域が物理ホストに存在すること。商用版だと[100GB](https://access.redhat.com/documentation/ja-jp/red_hat_hyperconverged_infrastructure_for_virtualization/1.8/html/upgrading_red_hat_hyperconverged_infrastructure_for_virtualization/prerequisites)と書かれていますが、対話形式のデプロイでは51GB以上を入力するように求められるので多少足りなくてもOKなはず。（ケチるところでもないけど）

# 作業の流れ
1. 事前準備
1. EngineVMのバックアップ
1. 物理ホスト１号機をCentOS 8で再インストール
1. 物理ホスト１号機をGlusterに再組み込み
1. 新規のGlusterボリューム作成
1. 物理ホスト１号機でoVirt 4.4のEngine VMをデプロイ
1. 残りの物理ホストもCentOS 8インストール＆Gluster再組み込み＆再インストール
1. クラスター＆データセンター互換バージョン更新


# 事前準備
1. NFSベースのストレージドメインのデタッチ<br>
物理ホストにNFSベースのストレージドメイン（ISO またはエクスポートドメイン）を置いている場合はデタッチします。必要な中身を退避した後、ストレージドメインをメンテナンスモードに切り替え→デタッチします。<br>
なお、物理ホストは全部再インストールするので最終的には作り直してリストアすることになります。
1. 管理ネットワーク（ovirtmgmt）以外のネットワークの必須プロパティを無効にします。

# バックアップ
バックアップは２種類あります。

## EngineVMのバックアップ
いきなり公式アップグレード手順から逸れますが、EngineVMのバックアップを以下で取得

物理ホストの１台にsshログインしてローカルメンテナンスモードに切り替え

```
$ sudo hosted-engine --set-maintenance --mode=local
```
EngineVMにsshログインしてバックアップを取得

```
$ sudo engine-backup --scope=all --mode=backup \
  --file=backup4310.bck --log=backuplog.log
```
取得したバックアップを別サーバ（oVirtクラスタ外）に退避

```
$ scp backup4310.bck backup.example.co.jp:~/
```

物理ホスト１台をメンテナンスモードにしてEngineVMのバックアップを取得するのは以前（oVirt 4.2の頃）の手順ですが、これでOKでした。
なお、この時ローカルメンテナンスにした物理ホストは最初にCentOS 8を再インストールする物理ホストになります。

## 物理ホスト１号機のGlusterノードUUIDとpeer情報のバックアップ
物理ホスト１号機にsshログインしてGluster関連の以下をバックアップし、oVirtクラスタ外に退避します。

```
$ ssh "物理ホスト１号機"
$ sudo tar cf - /var/lib/glusterd/glusterd.info /var/lib/glusterd/peers/ | \
  xz -9 > host1-backup.tar.xz
$ scp host1-backup.tar.xz backup.example.co.jp:~/
```

# oVirt 4.3のEngineVMの停止
バックアップが完了したら物理ホスト（どこでもいい）にsshログインして、グローバルメンテナンスモードに切り替え

```
$ ssh "物理ホスト（どれでもいい）"
$ sudo hosted-engine --set-maintenance --mode=global
```
この時点で、EngineVMの冗長化は機能しなくなります。
EngineVMにsshログインしてシャットダウン

```
$ ssh "EngineVM"
$ sudo shutdown -h now
```
EngineVMが停止するとEngineVM以外の仮想マシンの高可用性も機能しなくなります。

# 物理ホスト１号機をoVirt 4.4で再構築
ここから先に進むと簡単には引き返せなくなります。:smiley:
（大雑把に戻す方法は思いつくけど未検証）

## CentOS 8インストール

ホスト名とIPアドレスは同じものを引き継ぎます。<br>
ファイルシステム構成は再インストール前と同じ構成が望ましいですが、GlusterのBrickディレクトリ以外は厳密に同じでなくても大丈夫と思われます。

ネットワーク構成はovirtmgmt以外、再インストール前と同じにします。ただし、管理ネットワークのovirtmgmtはEngineVMのデプロイ中に指定したインターフェイスのIPアドレスを引き継いでブリッジとして自動的に作成されるので事前に作成してはいけません。
また、NetworkManagerの接続名（connection.id）はoVirtのネットワーク名と同じにします。（地味に重要）

CentOS 8インストール後、dnf updateでパッケージを更新し、（kernelが更新されるはずなので）再起動します。

```
$ sudo dnf -y update && sudo reboot
```

あと、警告が鬱陶しいので英語ロケールをインストール

```
$ sudo dnf -y install langpacks-en
```

## oVirt 4.4インストール
oVirt 4.4のリポジトリ設定をインストール

```
$ sudo dnf -y install https://resources.ovirt.org/pub/yum-repo/ovirt-release44.rpm
```
Gluster 6のリポジトリを追加

```
$ sudo dnf -y install centos-release-gluster6
```
oVirt 4.4標準のGluster 7リポジトリを無効化

```
$ sudo dnf config-manager --disable ovirt-4.4-centos-gluster7
```
oVirt HCI環境向けパッケージインストール

```
$ sudo dnf -y install cockpit-ovirt-dashboard vdsm-gluster
```

## Glusterのリストアと開始
退避しておいたバックアップを物理ホスト１号機にコピー

```
$ scp backup.example.co.jp:~/host1-backup.tar.xz .
```

元と同じパスにリストア

```
$ xz -dc host1-backup.tar.xz | sudo tar xf - -C /
```

再インストール前と同じ構成でBrickを準備します。
必要に応じてファイルシステム～fstab編集～マウント～Brickディレクトリを作成し、

```
$ lvcreate -L100g -n bricklv vgname
$ sudo mkfs.xfs /dev/mapper/vgname-bricklv
$ echo '/dev/mapper/vgname-bricklv /path/to/mountpoint xfs  defaults 0 0' | sudo tee -a /etc/fstab
$ sudo mount /path/to/mountpoint
$ sudo mkdir -p /path/to/mountpoint/brick
```
Brickディレクトリのowner/groupをvdsm:kvmに設定

```
$ sudo chown vdsm:kvm /path/to/mountpoint/brick
```
インデックスディレクトリ（？）作成

```
$ sudo mkdir -p /path/to/mountpoint/brick/.glusterfs/indices
```
まだ再インストールしていない、２号機 or ３号機で全てのBrickディレクトリの拡張属性 `trusted.glusterfs.volume-id` を確認

```
# ここだけ物理ホスト２号機 or ３号機で実行
# 全てのGlusterボリュームに対して行う
$ sudo getfattr -d -m. -ehex /path/to/brick | grep trusted.glusterfs.volume-id
getfattr: Removing leading '/' from absolute path names
trusted.glusterfs.volume-id=0x41d13b6671de4a289e28b87e13a52226 # この16進数をメモ
```
確認したtrusted.glusterfs.volume-id属性を物理ホスト１号機で作成したBrickディレクトリに設定

```
# 確認した個々のGlusterボリュームのtrusted.glusterfs.volume-idを
# 物理ホスト１号機のBrickディレクトリそれぞれに設定
$ sudo setfattr -n trusted.glusterfs.volume-id \
  -v 0x41d13b6671de4a289e28b87e13a52226 /path/to/mountpoint/brick
```
Firewall設定

```
$ sudo firewall-cmd --add-service glusterfs
$ sudo firewall-cmd --add-service glusterfs --permanent
```
hostsにGlusterノードのエントリを追加

```
$ sudo vi /etc/hosts
```
サービス起動

```
$ sudo systemctl enable --now glusterd.service
$ sudo systemctl enable --now glustereventsd.service
```
物理ホスト１号機自身のBrickが起動していることを確認

```
$ sudo gluster volume status
```
ボリューム修復

```
# 全てのGlusterボリュームに対し実行
$ sudo gluster volume heal "ボリューム名" full
```
修復結果確認

```
# 全てのGlusterボリュームに対し実行
$ sudo gluster volume heal "ボリューム名" info
```

Brickは起動しているのにヒーリングが動かない場合は

1. １号機のBrickを一旦削除
1. Brickディレクトリも削除
1. １号機のBrick追加

とやると修復出来ます。


# 新しいEngineVMのデプロイ

## Glusterボリューム追加
oVirt 4.4の新しいEngineVMのデプロイ先となるGlusterボリュームを新規作成します。
パーティション作成、LV作成、ファイルシステム作成等は構成によるので割愛。肝心のGlusterボリューム作成は以下

```
$ sudo gluster volume create "新EngineVM用Volume" replica 3 \
  "物理ホスト１号機Glusterノード名":/path/to/brick \
  "物理ホスト２号機Glusterノード名":/path/to/brick \
  "物理ホスト３号機Glusterノード名":/path/to/brick
```
作成したボリュームに仮想マシン用のパラメータを設定

```
$ sudo gluster vol set "新EngineVM用Volume" cluster.data-self-heal-algorithm full
$ sudo gluster vol set "新EngineVM用Volume" cluster.eager-lock               enable
$ sudo gluster vol set "新EngineVM用Volume" cluster.choose-local             off
$ sudo gluster vol set "新EngineVM用Volume" performance.low-prio-threads     32
$ sudo gluster vol set "新EngineVM用Volume" performance.strict-o-direct      on
$ sudo gluster vol set "新EngineVM用Volume" network.ping-timeout             30
$ sudo gluster vol set "新EngineVM用Volume" network.remote-dio               disable
$ sudo gluster vol set "新EngineVM用Volume" performance.read-ahead           off
$ sudo gluster vol set "新EngineVM用Volume" performance.io-cache             off
$ sudo gluster vol set "新EngineVM用Volume" performance.quick-read           off
$ sudo gluster vol set "新EngineVM用Volume" storage.owner-uid                36
$ sudo gluster vol set "新EngineVM用Volume" storage.owner-gid                36
$ sudo gluster vol set "新EngineVM用Volume" cluster.server-quorum-type       server
$ sudo gluster vol set "新EngineVM用Volume" features.shard                   on
$ sudo gluster vol set "新EngineVM用Volume" cluster.shd-max-threads          8
$ sudo gluster vol set "新EngineVM用Volume" cluster.shd-wait-qlength         10000
$ sudo gluster vol set "新EngineVM用Volume" cluster.locking-scheme           granular
$ sudo gluster vol set "新EngineVM用Volume" cluster.granular-entry-heal      enable
$ sudo gluster vol set "新EngineVM用Volume" client.event-threads             4
$ sudo gluster vol set "新EngineVM用Volume" server.event-threads             4
$ sudo gluster vol set "新EngineVM用Volume" performance.client-io-threads    on
$ sudo gluster vol set "新EngineVM用Volume" performance.stat-prefetch        off
```
なお、公式の設定は[こちら](https://access.redhat.com/documentation/en-us/red_hat_gluster_storage/3.4/html/configuring_red_hat_virtualization_with_red_hat_gluster_storage/chap-Hosting_Virtual_Machine_Images_on_Red_Hat_Storage_volumes#proc-To_Configure_Volumes_Using_the_Command_Line_Interface)で、`/var/lib/glusterd/groups/virt`に`storage.owner-uid`,`storage.owner-gid`の設定を追加したものになっており、上記とは微妙に異なります。（上記はCockpitからデプロイした設定＋oVirtのバージョンアップで変わった設定＋バグ回避設定のミックス）

パラメータ設定完了したらボリュームを起動

```
$ sudo gluster vol start "新EngineVM用Volume"
```

## EngineVMのデプロイ

CentOS 8を再インストールした物理ホスト１号機で新しいEngineVMをデプロイします。
物理ホスト１号機にログインして、EngienVMのイメージをダウンロード

```
$ ssh "物理ホスト１号機"
$ sudo dnf -y install ovirt-engine-appliance
```
oVirt 4.3のEngineVMのバックアップを退避先からコピー

```
$ scp backup.example.co.jp:~/backup4310.bck .
```
EngineVMのデプロイは対話形式ですが、選択肢がない設定は事前に応答ファイルに記載しておきます。
以下の内容（**YourClusterName**をクラスター名、**YourDataCenterName**をデータセンター名、**YourEngineVMFQDN**をEngineVMのFQDNに置き換える）を`he-deploy-answer.conf`という名前で作成

```
[environment:default]
OVEHOSTED_ENGINE/clusterName=str:YourClusterName
OVEHOSTED_ENGINE/datacenterName=str:YourDataCenterName
OVEHOSTED_NETWORK/fqdn=str:YourEngineVMFQDN
```
デプロイ中にネットワーク切れても大丈夫なようにtmux起動

```
$ tmux
```
バックアップデータのリストア＋応答ファイル指定でEngineVMのデプロイ開始

```
# インターネットに直接出られる環境の場合
$ sudo hosted-engine \
   --deploy --restore-from-file=backup4310.bck \
   --config-append=he-deploy-answer.conf
```
プロキシ環境下の場合は以下のように環境変数とオプションを追加します。

```
# プロキシ環境下の場合
$ sudo http_proxy="Proxy IP":"Proxy Port" https_proxy="Proxy IP":"Proxy Port" no_proxy="EngineVMのFQDN" hosted-engine \
   --deploy --restore-from-file=backup4310.bck \
   --config-append=he-deploy-answer.conf \
   --ansible-extra-vars=he_offline_deployment=true
```
なお、`--ansible-extra-vars=he_offline_deployment=true`というオプションはoVirt 4.4.3から[追加](https://bugzilla.redhat.com/show_bug.cgi?id=1851860)されたものなので古いバージョンでは使えません。（oVirt 4.4.2以下は色々小細工が必要だった）

デプロイは対話形式で応答ファイルに書かれていない事は逐一入力を求められますが、oVirt 4.3.10と変わらないので割愛します。
デプロイ先のストレージ選択の所は新規追加したGlusterボリュームを選択します。

```
Please specify the storage you would like to use (glusterfs, iscsi, fc, nfs)[nfs]: glusterfs
Please specify the full shared storage connection path to use (example: host:/path): "物理ホスト１号機のGlusterノード名":/"新規追加したGlusterボリューム名"
If needed, specify additional mount options for the connection to the hosted-engine storagedomain (example: rsize=32768,wsize=32768) []: backup-volfile-servers="物理ホスト２号機のGlusterノード名":"物理ホスト３号機のGlusterノード名"
```
デプロイが成功するとブラウザで管理画面にログイン出来るようになります。

# 物理ホスト１号機のネットワーク設定

物理ホスト１号機のネットワークはovirtmgmt以外は"Unmanaged"になっているので、（事前準備で"必須"を外しておいたのはこれが理由）
![unconfigured-host-network.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/147346/86d9a98a-aae4-21a7-a1b7-7bbe551b4604.png)
物理ホスト１号機の[ホストネットワーク設定]で全て設定しておきます。
なお、oVirt 4.4ではoVirtのネットワーク名==NetworkManagerの接続名（connection.id）となるのですが、oVirtのネットワーク名と異なるNetworkManager接続名を作成しIPを付与していた場合、

1. oVirtネットワーク名で新しいNetworkManagerの接続設定が作られる
1. 作成した接続設定にIPアドレスを設定しようとしたところで重複IPでエラー

になります。
oVirt 4.3の頃は物理ホストのネットワークはNetworkManager管理外となっており、上記のオペレーションを行ってもNetworkManagerの設定削除→管理外設定が作られるだけで平気だったようですが、oVirt 4.4では**処理途中で**エラーになります。

その２へ続く