Patroni 是如何工作的?

前言

昨天有朋友說,能不能簡單介紹一下 Patroni 是如何工作的。找了一圈資料,覺得有 Zalando 的一個 PPT(Patroni in 2019: What's New and Future Plans)寫的不錯,我們就來簡單的介紹一下它的工作原理。

Patroni 是如何工作的

傳統的流複製一個很大的弊病,是不能夠提供一個完整的高可用性解決方案。當主機發生異常 hang 死或者宕機的時候,沒辦法進行切換。

一個簡單的思路其實就是編寫腳本,定期的去檢查主服務器的狀態,如果主服務器不可用,那麼我們就把備庫提升爲主庫。但是這其實也是有問題的,有時候網絡可能發生了一些閃斷,這期間備庫就無法訪問主庫了。然後編寫的腳本檢測主服務器狀態異常,就把備庫提升爲了主庫。但是主庫現在仍然對外部客戶端可訪問,這就造成了腦裂的情況。

所以後面的思路就是使用第三服務器 witness,同時監控主庫和備庫,主庫和備庫上都有 agent,實時跟 witness 進行心跳確認,一旦主庫出現問題,witness 就能清楚的知道情況,然後做故障轉移。但是這種方案也是有弊病的,首先 witness 也是有可能會出問題的,此時再出現故障就無法發生切換。另外一個情況就是主庫和 witness 出現了網絡問題,無法訪問主庫的情況下,witness 也會提升備庫,但是主庫其實對外訪問是正常的。

| | | | --- | --- |

由於上述情況的出現,我們需要一種更加強大的現代化方法來確認主庫和從庫是否存活,於是我們採用了 etcd 這樣的基數節點集羣來實現。

爲什麼要是基數節點? 假設三個節點,如果有兩個節點同意我們的主要節點不可用,則這兩個節點具有多數票。如果是四個節點,就必須獲取到三個節點的票,才能獲得多數票,如果是兩票對兩票則是平手,還需要再進行投票。所以基數在投票環節上具備天然的優勢。

在這個圖中,我們的數據庫是一主兩從的流複製架構,節點 A 是主節點,B 和 C 是從節點。節點 A 會定期向 etcd 發送請求以更新領導者密鑰,默認情況是 10s 更新一次(這是由參數 loop_wait 控制),更新的時候帶了一個 TTL,前面說過代表着生存時間。這裏面有一個公式:

TTL > = loop_wait + retry_timeout * 2

這個機制是這樣的,patroni 進程每隔 10 秒 (loop_wait) 都會更新 Leader key 還有 TTL,如果 Leader 節點異常導致 patroni 進程無法及時更新 Leader key,則會重新進行 2 次嘗試(retry_timeout)。如果嘗試了仍然無效。這個時候時間超過了 TTL(生存時間)。領導者密鑰就會過期,然後觸發新的選舉。

發生新的選舉很簡單,節點 b 和節點 c 此時會收到通知,當前已經沒有領導者了,我們必須舉行新的選舉。

Patroni 之間也通過 rest api 互相訪問。他們首先會和曾經的領導者通信,會發現訪問超時,然後他們通過 rest api 訪問 Patroni 進程知道自己的 wal_position 位置。假設節點 b 和節點 c 現在都處於相同的 wal_position,都等於 100,那麼他們會同時訪問 etcd,發送創建密鑰的請求,然後開始領導爭奪戰。

兩個節點到 etcd 之間,Node C 率先創建了密鑰。Node C 上面就執行 promote,成爲了新的主庫,Node B 將成爲新的從庫。它會選擇從 Node C 來進行復制。然後 Node C 就成爲了 leader。

May 26 15:16:23 133e0e204e208 patroni: 2021-05-26 15:16:23,865 INFO: no action.  i am a secondary and i am following a leader
May 26 15:16:29 133e0e204e208 patroni: 2021-05-26 15:16:29,436 INFO: Got response from postgres2 http://133.0.204.207:8008/patroni: {"state""running""postmaster_start_time""2021-05-25 16:27:00.818 CST""role""replica""server_version": 130002, "cluster_unlocked": true, "xlog"{"received_location": 3909174320, "replayed_location": 3909174320, "replayed_timestamp""2021-05-26 15:06:09.766 CST""paused": false}"timeline": 13, "database_system_identifier""6962171552537974697""patroni"{"version""2.0.2""scope""patnori-test"}}
May 26 15:16:29 133e0e204e208 patroni: 2021-05-26 15:16:29,449 WARNING: Request failed to postgres1: GET http://133.0.204.206:8008/patroni (HTTPConnectionPool(host='133.0.204.206'port=8008): Max retries exceeded with url: /patroni (Caused by ProtocolError('Connection aborted.', ConnectionResetError(104, 'Connection reset by peer'))))
May 26 15:16:29 133e0e204e208 patroni: 2021-05-26 15:16:29,455 WARNING: Could not activate Linux watchdog device: "Can't open watchdog device: [Errno 2] No such file or directory: '/dev/watchdog'"
May 26 15:16:29 133e0e204e208 patroni: 2021-05-26 15:16:29,458 INFO: promoted self to leader by acquiring session lock
May 26 15:16:29 133e0e204e208 patroni: server promoting
May 26 15:16:29 133e0e204e208 patroni: 2021-05-26 15:16:29,461 INFO: cleared rewind state after becoming the leader
May 26 15:16:30 133e0e204e208 patroni: 2021-05-26 15:16:30,471 INFO: Lock owner: postgres3; I am postgres3

我們模擬一下,把 Node A 直接關閉,關閉一個 Patroni 節點這裏切換的速度是非常快的。我們看 Node C 的日誌,其中節點 C 通過 Rest API 訪問自己的 patroni 進程,獲取自己的 wal_position,同時它還會用 Rest API 訪問曾經的領導者 Node A,發現訪問超時。緊接着它會去激活 watchdog,我們這個環境沒有配置 watchdog,所以它這裏出現了一個 WARNING。同時它會訪問 etcd 創建領導者密鑰,如果它創建成功,它將獲取會話鎖將自己提升爲 leader。整個日誌的過程和前面圖片介紹的完全吻合。

後記

瞭解一下它的工作原理,方便我們後面進行高可用測試。

參考文檔 https://www.postgresql.eu/events/pgconfeu2019/sessions/session/2717/slides/218/Patroni%20in%202019_%20What's%20New%20and%20Future%20Plans.pdf

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/8ZipIAd_jX1ykd7j6ZLK4g