Corosync の quorum  の設定

CentOS 7 のリポジトリからサクッとインストールできる下記で試しています。

- corosync-2.3.4-7.el7_2.1
- pacemaker-1.1.13-10.el7_2.2
- resource-agents-3.9.5-54.el7_2.9

`man corosync.conf` と `man votequorum` から理解出来た部分をそれっぽく意訳しています。

# corosync.conf(5)

`man corosync.conf` 曰く。

`quorum` セクションの `provider` ディレクティブで quorum アルゴリズムを指定できる。今のところ `corosync_votequorum` だけが指定できる。

```
quorum {
    provider: corosync_votequorum
}
```

詳細は `votequorum(5)` を参照。

# votequorum(5)

`man votequorum` 曰く。

クラスタノードの過半数がどれだけであるかを知るために、クラスタの総票数を `quorum` セクションの `expected_votes` で指定するか、`nodelist` セクションでノードを一覧しておく必要がある。どちらも無い場合は `votequorum` は無効になる。両方が指定された場合は `quorum.expected_votes` が `nodelist` から計算された値を上書きする。

例えば、8 ノードクラスタでは次のように。

```
quorum { 
    provider: corosync_votequorum
    expected_votes: 8
}
```

3 ノードクラスタで `nodelist` セクションを書くのであれば次のように。

```
quorum { 
    provider: corosync_votequorum
} 

nodelist {
    node {
        ring0_addr: 192.168.1.1
    }
    node {
        ring0_addr: 192.168.1.2
    }
    node {
        ring0_addr: 192.168.1.3
    }
}
```

## two_node

`two_node: 1` を指定すると 2 ノードクラスタが可能になる。

```
quorum {
    provider: corosync_votequorum
    expected_votes: 2
    two_node: 1
}
```

または、

```
quorum {
    provider: corosync_votequorum
    two_node: 1
}

nodelist {
    node {
        ring0_addr: 192.168.1.1
    }
    node {
        ring0_addr: 192.168.1.2
    }
}
```

2 ノードクラスタは特別な配慮が必要なユースケース。標準的な 2 ノードクラスタでは各ノードが 1 票を持つのでクラスタ内の合計は 2 票。単純に過半数を計算すると `votes * 50% + 1` で 2 となる。これは、過半数を得るためには常に両方のノードが生きていないことを意味する。

`two_node: 1` を有効にするとクオラムは人為的に？ 1 に設定される。


`two_node: 1` を設定すると自動的に `wait_for_all: 1` が設定される。`wait_for_all: 0` を明示的に指定することで上書きすることができる。

2 より多いノードがクラスタに参加した場合 `two_node` は自動的に無効になる。

## wait_for_all

`wait_for_all: 0` の場合（デフォルト）、過半数の投票が集まるとその他のノードの参加を待たずにクラスタは開始する。

例えば、8 ノードクラスタの場合、過半数は 5 票なので 5 ノードがクラスタに参加すると残りの 3 ノードを待たずにクラスタは開始する。

`wait_for_all: 1` にすると、すべてのノードが少なくとも 1 回は揃うまで開始が待たされる。

```
quorum {
    provider: corosync_votequorum
    expected_votes: 8
    wait_for_all: 1
}
```

## last_man_standing

通常、クラスタは過半数の票が集まらなければ停止するが、`last_man_standing` を設定すると、クラスタの総票数とクオラムが動的に再計算されるようになる。`last_man_standing_window` は再計算されるまでのミリ秒単位の時間。

```
quorum {
    provider: corosync_votequorum
    expected_votes: 8
    last_man_standing: 1
    last_man_standing_window: 20000
}
```

例えば、8 ノードクラスタの場合、`expected_votes` は 8 で `quorum` は 5 なので 3 台までの障害しか許容できないが、`last_man_standing` を設定すると、次のように動的に `expected_votes` と `quorum` が計算されるようになる。

1. クラスタが 8 ノードで稼働している
  - `expected_votes: 8 / quorum: 5`
2. 3 ノードが死んで残り 5 ノードになった
3. `last_man_standing_window` の時間が経過後に `expected_votes` と `quorum` が再計算される
  - `expected_votes: 5 / quorum: 3`
4. さらに 2 ノードが死んで残り 3 ノードになった
3. `last_man_standing_window` の時間が経過後に `expected_votes` と `quorum` が再計算される
  - `expected_votes: 3 / quorum: 2`
6. さらに 1 ノードが死んで残り 2 ノードになった
3. `last_man_standing_window` の時間が経過後に `expected_votes` と `quorum` が再計算される
  - `expected_votes: 2 / quorum: 2`

ここから、さらに 1 ノードクラスタにダウングレードするためには後述の `auto_tie_breaker` も設定する必要がある。

## auto_tie_breaker

一般的な動作ではクラスタは「50% - 1」ノードまでの故障を許容することができる（各ノードが 1 票を持っている場合）。

`auto_tie_breaker` を有効にすると、クラスタは 50% ノードまでの故障を許容できるようになる。

`auto_tie_breaker` のいデフォルトでは、クラスタが 50% のノードに分断されたときに最も小さいノード ID を持つセットが過半数に達しているとみなされる。

例えば、8 ノードクラスタで下記の 2 つのセットに分断された場合、

- `A = {1, 3, 5, 7}`
- `B = {2, 4, 6, 8}`

もっとも小さいノード ID は `1` であるため、`A` が過半数に達しているとみなされる。

`auto_tie_breaker_node` を指定することで条件を変更することができる。指定できる値は `lowest|highest|<list of node IDs>` のいずれかで、`lowest` や `highest` はそのままの意味、ノードの ID を指定した場合はそのノードのあるセットがクオラムに達する。ノード ID を空白区切りで複数指定した場合はクラスタに残っているノードの中で一番左の ID を持つセットがクオラムに達する。

```
quorum {
    provider: corosync_votequorum
    expected_votes: 8
    auto_tie_breaker: 1
    auto_tie_breaker_node: 1 3 5 7
}
```

## allow_downscale

この機能は不完全で、現在サポートされていない。