Redhat

Systemd 和災難恢復備用系統

  • November 22, 2021

我們使用 systemd 在生產環境中執行各種服務。(呃……)

我們正在建構一個匹配的“災難恢復”站點,該站點將安裝相同的應用程序——使用相同的 systemd-units 以在發生災難時啟動其各種組件。

這種 DR 環境是“熱的”,準備在短時間內接管(越短越好)——從而成為生產本身。然後,當“災難”解決後,另一個環境將成為 DR。

我的問題是,如何讓這些 systemd-services 準備好啟動,但直到某個條件變為真才真正啟動?

為了得出結論,特定站點目前是主要站點(生產),命令 ( amIthePrimary) 需要執行並以 0 退出程式碼退出。檢查既簡單又快速——可以每分鐘執行一次。但是,因為它需要執行命令,所以 systemd 沒有Condition提供它

我是否將該命令放入每個單元的 ExecPre中,或者這會成為一個嘈雜的錯誤,不必要地惹惱管理員?我是否將其與所有其他服務一起放入一個獨立的單元中Require

另外,一旦條件為真——服務啟動——我如何繼續檢查它,所以它們都將關閉,它是否應該再次變為假?

由於您的案例是非常自定義的,並且您的需求將來可能會發生變化,所以您為什麼不執行以下操作…

failover-manager在每分鐘執行一次的兩台機器上創建一個新的 systemd 計時器(例如)。systemd 計時器將定期啟動關聯的一次性 systemd 服務。

該一次性 systemd 服務可以只執行一個包含您的邏輯的 bash 腳本:

  • 執行amIthePrimary檢查

  • 如果是主要的,請啟動您的 systemd 服務並檢查它是否已正確啟動。

  • 如果不是主要的,則停止您的 systemd 服務(如果它正在執行)。

  • 如果您的腳本無法啟動/停止/驗證執行,那麼它應該會失敗(可監控),否則會成功。

  • 這個計時器腳本不需要輸出任何東西(噪音方面),除非它做出改變,即:

    • “我是主要的,但服務沒有執行。正在啟動……等待幾秒鐘……經過驗證的服務正在執行且沒有錯誤。” 或者
    • “我不是主要的,但服務正在執行。停止。”

通過這種方式,您始終知道您可以監控您的正常/計時器檢查是否正在無錯誤地執行。如果您的計時器服務有問題(非零退出),您可以通過監控來捕捉它。您可以單獨監控主應用程序服務的故障。

如果您的需求發生變化,您可以輕鬆調整計時器腳本或其執行頻率。

可能有更簡潔的方法可以做到這一點,但它們可能取決於您amIthePrimary檢查背後的任何事物所生成的事件……並且您沒有提供任何詳細資訊。即事件驅動的故障轉移而不是輪詢。

您也可以將您的amIthePrimary檢查放入ExecStartPre=… 但是當它無法阻止服務啟動時,您的服務將處於 FAILED 狀態,這可能會混淆您的監控,因為它不是嚴重失敗,而是故意失敗。因此,您可能更喜歡使用計時器方法,因為這樣您就可以分別監控計時器程序和主服務程序。計時器應始終執行、活動且不會失敗。您的服務(如果正在執行)永遠不應處於失敗狀態或監控應該關閉。還有一個問題是如何從監控的角度知道服務是否應該執行,但這超出了問題的範圍。

更新 - 包括範例實現範例

未經測試,但只是為了讓我的建議更清楚。

failover-manager.sh

假設這個腳本部署到/opt/failover-manager/failover-manager.sh

#!/bin/bash

# expected ENV.  Provided by the service that starts this script.
#
# APP_SERVICE (your main application)
# SECONDS_TO_START (e.g. some java apps start very slowly)

if [ -z "$APP_SERVICE" -o -z "$SECONDS_TO_START" ]; then
   echo "Missing environment"
   exit 1
fi

function is_running {
   systemctl is-active --quiet $1
   return $?
}

if amIthePrimary; then
   if is_running $APP_SERVICE; then   # no change, no log
       exit 0
   else
       echo "I AM primary, but service NOT running.  STARTING..."
       systemctl start $APP_SERVICE
       sleep $SECONDS_TO_START
       if is_running $APP_SERVICE; then 
           echo "Verified service is STARTED without error: $APP_SERVICE."
           exit 0
       else
           echo "Service $APP_SERVICE has not yet STARTED after $SECONDS_TO_START seconds."
           exit 1
       fi
   fi
else
   if is_running $APP_SERVICE; then 
       echo "I am NOT primary, but service IS running.  Stopping..."
       systemctl stop $APP_SERVICE
       sleep $SECONDS_TO_START
       if is_running $APP_SERVICE; then 
           echo "Service $APP_SERVICE has not yet STOPPED after $SECONDS_TO_START seconds."
           exit 1
       else
           echo "Verified service is STOPPED: $APP_SERVICE."
           exit 0
       fi
   else   # no change, no log
       exit 0
   fi
fi

failover-manager.timer

[Unit]
Description=Timer that starts failover-manager.service
Requires=failover-manager.service

[Timer]
Unit=failover-manager.service
# every 1 minute
OnCalendar=*:0/1
AccuracySec=1s
Persistent=true


[Install]
WantedBy=timers.target

failover-manager.service

這傢伙是由上面的計時器執行的。

[Unit]
Description=Checks if we need to start or stop our application.

[Service]
Type=oneshot
Environment=APP_SERVICE="my-application.service" SECONDS_TO_START="5"    
WorkingDirectory=/opt/failover-manager/
ExecStart=/opt/failover-manager/failover-manager.sh

User=root
Group=root

純係統選項?

如果您正在尋找一種純粹的 systemd 機制來以乾淨的方式完成此任務,那可能是不可能的。

您的案例是自定義的,IMO 超出了 systemd 的範圍。

ExecStartPre因此,您可以在使用或使用requires/類型依賴機制時“破解”它wants……但所有這些方法都依賴於一個程序,或者由於失敗而處於停止狀態(中斷監控……它是預期的失敗還是失敗的失敗) …或者那個程序被“某事”啟動/停止,該“某事”知道系統世界之外的某事。後者不會破壞監控,但確實需要 systemd 之外的東西,而我提出的是一種方法。

備擇方案

就像@anx 建議的那樣……也許重新設計您的 DR 故障轉移的工作方式。

這也是我們採取的方法。如果我們有一個備用盒/雲/機架/等,那麼我們希望確保一切都已經在執行(例如服務等)。

那麼問題就是……如何進行切換。

有兩種常見的方式可以完成故障轉移到備用端點……

1 - DNS 故障轉移

為您的關鍵端點設置一個較低的 DNS ttl(記憶體時間),並在檢測到故障時更新您的 DNS 記錄以指向備用端點(例如 CNAME、A、AAAA DNS 更新)。

許多託管 DNS 提供商(例如 dnsmadeeasy、dynect)將此作為其服務(檢測和故障轉移)的一部分提供。但當然,您可以使用自己的 DNS 或任何 DNS 提供商來實現此功能,使您能夠設置低 TTL 並輕鬆手動或自動(監控 + DNS API)更新您的 DNS 記錄。

這裡的一個潛在問題是您可能會擔心機器人會向“非活動”端點發出請求。這肯定會發生,但如果您的應用程序設計得很好,它不會破壞任何東西,讓一些請求進入備用 DR 端點。

好消息是這迫使您考慮如何使您的應用程序架構在接收流量的多個並發端點(共享數據庫、複製等)方面更加健壯。

如果這很重要,您可以潛在地添加 iptables 規則來管理它…但是您可能會遇到與以前相同的問題…如何觸發更改(因為現在需要更改的是 DNS 和 iptables發生故障轉移)。

2 - 負載平衡器故障轉移

備用伺服器在負載均衡器中不活動並且可以快速添加/交換到負載均衡器後面的活動伺服器池中是很常見的。

在這種情況下,負載均衡器或第三個組件可以管理健康檢查並更新負載均衡器配置以將健康的伺服器替換為不健康的伺服器。

這不適用於 DR 案例,因為負載平衡器通常是本地機架或數據中心。因此,對於 DR,您可能最好建構基於 DNS 的故障轉移到不同的數據中心/區域。

引用自:https://serverfault.com/questions/1061100