MySQL 到底能不能放到 Docker 裏跑?

前言node

前幾月常常看到有 MySQL 到底能不能放到 Docker 裏跑的各類討論。這樣作是錯的!這樣作是對的!說錯的理由也說了一大堆,說對的思想也很明確。你們都有道理。可是我本人以爲這樣的討論落地意義不大。由於對與錯仍是要實踐來得出的。web

因此同程旅遊也很早開始了 MySQL 的 Docker 化實踐,到目前已經有超一千多個 MySQL 實例在 Docker 平臺安全穩定地跑着,DB 運維能力發生了質的提升(DBA 不再用擔憂刪庫跑路了)。算法

固然這樣是否是能夠證實以前的討論結論——是對的。我想也不必定,由於咱們還只是一隻在學飛行的小鳥,還要更多的學習,因此咱們特將咱們在 MySQL 的 Docker 化上的實踐分享給你們。數據庫

背景介紹後端

同程旅遊早期的數據庫都以 MSSQL 爲主,這個產品有個特色就是 UI 操做很棒。可是批量和自動化管理很難作,人力的工做不少。後來逐漸替換爲 MySQL 後也是按照傳統的運維方式管理。致使大部分的工做須要人肉運維。安全

固然像咱們早期使用過的 MSSQL 也是有優勢的:就是單機性能比較好,在當年那個資源不夠的年代裏咱們常能夠在高可用的實例上運行多個庫。這種狀況下物理機數量與實例數量仍是比較可控的,相對數量比較少,人肉運維徹底能夠應對。服務器

可是 MSSQL 的缺陷也不少,好比作水平拆分比較困難,致使數據庫成爲系統中較大的一個瓶頸。但在咱們使用 MySQL+ 中間件(咱們作這個中間件也是下了很多心思的,之後能夠分享一下)作水平拆分後就開始解決了這個瓶頸。網絡

水平拆分的引入也帶來了一個小缺點,就是會形成數據庫實例數量大幅上升。舉個例子咱們作 1024 分片的話通常是作 32 個 node,一主一從是必須的(大部分狀況是一主兩從),那麼至少 64 個實例,再加上應急擴展和備份用的節點那就更多了(中間件的開發者更但願是 1024 片就是 1024 個實例)。架構

一次上線作一個 32node 分片擴展從庫,兩個 DBA 足足花了 4 個小時。另外,若是作單機單實例那確定更不行了,別的不說,成本也會是個大問題,且物理機的資源也未能較大化利用。何況由於 MySQL 單體的性能沒優點因此分片居多因此大部分狀況下並非每一個庫都能跑滿整個物理機的。即便有部分能跑滿整機資源的庫,它的多節點備份,環境一至性和運維動做統一等問題也會讓 DBA 一頭糟,忙碌又容易出錯的工做實際上是無心義的。運維

有了單機多實例運行 MySQL 實例的需求。單機多實例要思考的主要問題就是若是進行資源隔離和限制,實現方案有不少,怎麼選?KVM,Docker,Cgroups 是目前的能夠實現隔離主流方案。

KVM 對一個 DB 的隔離來講過重了,性能影響太大,在生產環境用不合適。這是由於 MySQL 運行的就是個進程並且對 IO 要求比較高,因此 KVM 不知足要求 (雖然優化之後 IO 能有點提高)。

cgroups 比較輕,雖然隔離性不是很高,但對於咱們的 MySQL 多實例隔離來講是徹底夠用了(Docker 的資源限制用的就是 cgroups)。可是咱們還想針對每一個 MySQL 實例運行額外的管理進程 (好比監控等等)。用 cgroups 實現起來會比較複雜,而且咱們還想讓實例管理和物理機區分開,那 cgroups 也放棄。

至於 Docker,那就很不錯了,那些裸用 cgroups 的麻煩它都給搞定了。而且有 API 能夠提供支持,開發成本低。並且咱們能夠基於 Docker 鏡像來作部署自動化,那麼環境的一至性也可輕鬆解決。因此最終咱們選擇了 Docker 做爲雲平臺的資源隔離方案 (固然過程當中也作了不少性能、穩定性等的適配工做,這裏就不贅述了)。

下面兩個圖能夠形象展現這款產品帶來的革命性意義:

MySQL 到底能不能放到 Docker 裏跑?
固然要能稱之爲雲,那麼平臺最基本的要求就是具有資源計算、資源調度功能,且資源分配無需人工參與。對用戶來說,拿到的應該是直接可用的資源,而且天生自帶高可用、自動備份、監控告警、慢日誌分析等功能,無需用戶關心資源背後的事情。其次纔是各類平常的 DBA 運維操做需求服務化輸出。下面咱們就來說講咱們這個平臺是如何一步步實現的。

平臺實現過程

站在巨人的肩膀上

我一直認爲評價一款數據庫的優劣,不能只評價數據庫自己。咱們要綜合它的周邊生態是否健全,好比:高可用方案、備份方案、平常維護難度、人才儲備等等。固然對於一個雲平臺也同樣,因此咱們進行了短平快的試錯工做,將平臺分爲多期版本開發。第一個版本的開發週期比較短,主要用來試驗,因此咱們要儘量運用已有的開源產品來實現咱們的需求,或者對已有開源產品進行二次開發之後實現定製化的需求。如下是咱們當時用到的部分開源產品和技術。

MySQL 到底能不能放到 Docker 裏跑?
下面選幾個產品簡單說一下咱們經過它實現什麼功能:

Percona:咱們的備份、慢日誌分析、過載保護等功能都是基於 pt-tools 工具包來實現的。

Prometheus:性能優越且功能強大的 TSDB,用於實現整個平臺實例的監控告警。缺點是沒有集羣功能,單機性能是個瓶頸 (雖然單機的處理能力已經很強了),因此咱們在業務層面進行了 DB 拆分,實現了分佈式存儲及擴展。

Consul:分佈式的服務發現和配置共享軟件,配合 prometheus 實現監控節點註冊。

Python:管理 Docker 容器中 MySQL 實例的 agent 以及部分操做腳本。

Docker:承載 MySQL 實例並實現資源隔離和資源限制。

整體架構

MySQL 到底能不能放到 Docker 裏跑?
容器調度系統如何選擇

容器調度的開源產品主要有 Kubernetes 和 mesos,可是咱們並無選用這兩個。主要緣由是咱們內部已經開發了一套基於 Docker 的資源管理、調度的系統,至今穩定運行 2 年多了。這套架構稍做修改是符合需求的。

另外第三方的資源調度系統兼容咱們目前的高可用架構,其餘自動化管理有些難度,同時資源分配策略也須要定製化。因此最終仍是選擇採用了自研的資源調度管理。適合本身現狀的需求才是較好的。固然後面有機會作到計算調度和存儲調度分離的狀況下咱們可能會轉向 Kubernetes 的方案。

工做原理

咱們就拿建立集羣來舉例吧。當平臺發起一個建立集羣的任務後,首先會根據集羣規模 (一主一從仍是一主多從,或者是分片集羣) 肯定要建立的實例數量,而後根據這個需求按照咱們的資源篩選規則 (好比主從不能在同一臺機器、內存配置不容許超賣等等),從現有的資源池中匹配出可用資源,而後依次建立主從關係、建立高可用管理、檢查集羣複製狀態、推送集羣信息到中間件 (選用了中間件的狀況下) 控制中心、最後將以上相關信息都同步到 CMDB。

以上的每個工做都是經過服務端發送消息到 agent,而後由 agent 執行對應的腳本,腳本會返回指定格式的執行結果,這些腳本是由 DBA 開發的。這種方式的優點在於,DBA 比任何人都瞭解數據庫,因此經過這種方式能夠有效提高項目開發效率,也能讓 DBA 參與到項目當中去。開發只須要寫前臺邏輯,DBA 負責後端具體執行的指令。若是將來功能有變動或迭代的話,只須要迭代腳本便可,維護量極小。

資源的調度分配原則

通過對同程多年的 DB 運維數據分析獲得以下經驗:

CPU 較大超賣 3 倍,內存不超賣;

同一機房優先選擇資源最空閒的機器;

主從角色不容許在同一臺機器上;

如有 VIP 需求的主從端口須要一致,無 VIP 需求直接對接中間件的無故口一致的限制;

分片的集羣將節點分佈在多臺物理機上;

產品分類

MySQL 到底能不能放到 Docker 裏跑?
核心功能

MySQL 到底能不能放到 Docker 裏跑?
以上是已經上線的部分核心功能,還有不少功能就再也不一一展現。

備份恢復系統

MySQL 到底能不能放到 Docker 裏跑?
備份工具咱們是用 percona-xtrabackup。經過流備份的方式將數據備份到遠端的備份服務器。備份服務器有多臺,分別按照所屬機房劃分。

咱們提供了手工備份和定時備份來知足不一樣場景的需求。多實例備份必定要關注磁盤 IO 和網絡,因此咱們的備份策略會限制單個物理機上並行備份的數量,另外單個機房備份任務隊列的並行度也有控制,確保並行備份任務始終保持到咱們指定的數量。

假如整個機房並行的是 50 個任務,那麼這 50 個當中若是有 5 個提早備份完成,那麼會新加入 5 個等待備份的任務進入這個備份隊列。咱們後來改造了備份的存儲方式,直接將備份流入分式存儲。

監控告警系統

MySQL 到底能不能放到 Docker 裏跑?
在上線這套雲平臺前,咱們仍是用傳統的 zabbix 來實現監控告警的。zabbix 的功能的確很是強大,可是後端的數據庫是個瓶頸,固然能夠經過數據庫拆分的方式解決。

數據庫要監控的指標比較多,若是採集的項目比較多,zabbix 就須要加 proxy,架構愈來愈複雜,再加上和咱們平臺對接的成本比較高,對一些複雜的統計類查詢 (95 值、預測值等) 性能比較差。

因此咱們選了一款 TSDB——prometheus,這是一款性能極強、極其適合監控系統使用的時序性數據庫。prometheus 優勢就是單機性能超強。但凡事又有兩面性,它的缺點就是不支持集羣架構 (不過咱們解決了擴展的問題,下面會講到)。

prometheus 的使用應該是從一年前就開始的,那時候咱們只是把它做爲輔助的監控系統來使用的,隨着逐漸熟悉,愈來愈以爲這個是容器監控的絕佳解決方案。因此在上雲平臺的時候就選擇了它做爲整個平臺的監控系統。

監控數據採集

prometheus 是支持 pushgateway 和 pull 的方式。咱們選用了 pull 的方式。由於結構簡單,開發成本低的同時還能和咱們的系統完美對接。consul 集羣負責註冊實例信息和服務信息,好比 MySQL 實例主從對應的服務、Linux 主從對應的服務、容器註冊對應的服務。而後 prometheus 經過 consul 上註冊的信息來獲取監控目標,而後去 pull 監控數據。監控客戶端是以 agent 的形式存在,prometheus 經過 HTTP 協議獲取 agent 端採集到的數據。

監控指標畫圖

不得不說 grafana 是監控畫圖界的扛把子,功能齊全的度量儀表盤和圖形編輯器,通過簡單配置就能完成各類監控圖形的展現。而後咱們打通了雲平臺和 grafana 的關聯,用戶在雲平臺須要查看實例或集羣信息,只要點擊按鈕便可。

MySQL 到底能不能放到 Docker 裏跑?
MySQL 到底能不能放到 Docker 裏跑?
告警管理

告警管理分爲:告警發送、告警接收人管理、告警靜默等功能。prometheus 有一個告警發送模塊 alertmanager,咱們經過 webhook 的方式讓 alertmanager 把告警信息發送到雲平臺的告警 API,而後在雲平臺來根據後面的邏輯進行告警內容發送。

alertmanager 推過來的只是實例緯度的告警,因此咱們結合告警平臺的實例相關信息,會拼出一個多維信息的告警內容。讓 DBA 一看就知道是誰的哪一個集羣在什麼時間觸發了什麼等級的什麼告警。告警恢復後也會再發一次恢復的通知。

MySQL 到底能不能放到 Docker 裏跑?
alertmanager 也是功能強大的工具,支持告警抑制、告警路由策略、發送週期、靜默告警等等。有須要能夠自行配置。可是這種和平臺分離的管理方式不是咱們想要的,因此就想把 alertmanager 對告警信息處理的這部分功能集成到雲平臺內。

可是官方文檔並無說起到 alertmanager 的 API,經過對源碼的分析,咱們找到了告警管理相關的 API。而後 alertmanager 的原生 UI 上操做的功能完美移植到了咱們的雲平臺,同時新增了實例相關集羣名稱、負責人等更多緯度的信息。

下面是一些操做樣例:

當前告警:

MySQL 到底能不能放到 Docker 裏跑?
添加告警靜默:

MySQL 到底能不能放到 Docker 裏跑?
已建立的靜默規則:

MySQL 到底能不能放到 Docker 裏跑?
慢日誌分析系統

MySQL 到底能不能放到 Docker 裏跑?
慢日誌的收集是經過 pt-query-digest 每小時進行本地分析,分析完成之後將結果寫入慢日誌存儲的數據庫來完成的。固然若是用戶須要馬上查看當前慢日誌的狀況,也能夠在界面點擊慢日誌分析。分析完成後能夠在 UI 界面點擊慢日誌查看,就能看到該實例的慢日誌分析結果。它同時集成了 explain、查看 table status 等功能。

集羣管理

集羣管理做爲該平臺的核心功能之一,佔據了整個平臺 70% 的工做。這些功能就是 DBA 運維中常常須要用到的。咱們的設計思路是以集羣爲單位,因此同時只能操做一個集羣上的實例。這樣就不會在一個頁面上顯示過多無用的信息,看着亂還有可能致使誤操做。看了下圖中的這些功能就能更明白爲何要這麼設計了。

MySQL 到底能不能放到 Docker 裏跑?
圖中只是一部分,還有部分未展現出的功能 (集成中間件、Dashboard、黑屏診斷窗口等),在後版中功能更多。

高可用

高可用方案咱們使用了目前最流行的 MySQL 高可用方案 MHA。MHA 的優缺點就不在這裏講了,有 DBA 同窗的應該都已經很熟悉了。這裏我說一下咱們基於同程業務作的調整。

GTID

由於咱們主要使用的 MariaDB,可是 MHA 版本也是不能支持 MariaDB 的 GTID 切換。因此咱們在原有的基礎上作了改進,支持了 MariaDB 的 GTID。使用 GTID 之後靈活切換是一個方面,另一個方面是 sync_master_info 和 sync_relay_log_info 就不須要設置成 1 了 (MariaDB 不支持寫 table,只能寫 file),極大減小了從庫複製帶來的 IOPS。

切換時調整相關參數

咱們在切換時調整 sync_binlog 和 innodb_flush_log_at_trx_commit 參數,這兩個參數是決定數據落盤方式的,默認你們都是設置雙 1。這樣相對數據最安全,可是 IO 也較高。

雲服務的多實例部署會致使一臺物理機上既有 master 又有 slave。咱們確定不但願 slave 產生過高的 IO 影響到同機器的其餘 slave(雖然能夠 IO 隔離,可是優先下降沒必要要 IO 才靠譜)。因此理論上來講 Master 上面設置雙 1,slave 則能夠不這樣設置。可是切換後原來的 salve 可能會變成了 master。因此咱們默認 slave 非雙 1,在 MHA 切換的時候會自動將新 master 的這兩個參數設置爲 1。

哨兵

咱們在多個點部署了哨兵服務。這個哨兵是一個簡單的 API 服務,帶上響應的參數能夠請求到指定的實例。當 MHA manager 檢測到有 Master 沒法鏈接時,會觸發 secondary check 機制,帶着 master 相關信息請求哨兵節點的 API,根據哨兵節點返回狀況,若超過半數沒法鏈接則切換。不然放棄切換。

高可用切換對接 DB 中間件

MySQL 到底能不能放到 Docker 裏跑?
DB 中間件和 DB 經過物理 IP 鏈接,當發生高可用切換時將的 Master IP、Master port 信息推送到 DB 中間件控制中心,DB 中間件拿到配置後馬上下發並生效。

實例、庫遷移

MySQL 到底能不能放到 Docker 裏跑?
遷移功能初衷是爲了將平臺外的實例或者庫遷移到平臺裏面來,後來隨着逐漸使用發現這個功能可挖掘的空間很大,好比能夠作平臺內庫表拆分等需求。實現原理也很簡單,用 mydumper 將指定數據備份下來之後,再用 myloader 恢復到指定數據庫。

這是一個全量的過程,增量複製用的是用咱們本身開發的一個支持並行複製的工具,這個工具還支持等冪處理,使用更靈活。沒有用原生複製的緣由是,假如要將源實例多個庫中的一個庫遷移到目標實例,那麼原生複製就須要對 binlog 作複製過濾,這裏面涉及到配置修改,實例重啓,因此果斷不考慮。

實現過程並無高大上,可是徹底知足需求。固然 mydumper 和 myloader 也有一些問題,咱們也作了小改動之後才實現的。後面咱們計劃用流的方式去作數據導出導入 (相似於阿里開源的 datax)。

遷移完成,增量無延遲的狀況下,你們會關心遷移先後數據一致性的問題,咱們提供了自研的數據校驗工具。實測 300G 的數據校驗時間約爲 2 至 3 分鐘,快慢取決於開多少線程。

屏蔽底層物理資源

對用戶來說,平臺提供的是一個或一組數據庫服務,不須要關係後端的實例是在哪臺機器上。資源計算和調度所有由系統的算法進行管理。

提高資源利用率 (CPU、內存)

經過單機多實例,CPU 資源可超賣,有效提升 CPU 資源的利用。內存資源未超賣,可是能夠控制到每一個實例的內存使用,確保每一個實例都能有足夠的內存。如有剩餘內存,則繼續分配容器便可,不 OOM 的狀況下壓榨內存資源。

提高運維效率

效率的提高得益於標準化之後帶來的自動化。批量運維的成本很低。之前部署一套分片集羣須要花費將近 6 個小時 (不包含對接中間件的 1 到 2 個小時),而如今只須要 5 分鐘便可部署完成。而且部署完成之後會將提供一套中間件 +DB 分片集羣的服務。

精細化管理

平臺上線後有效提升了資源利用率,同時咱們按照 1 庫 1 實例的方式,能夠有效避免不一樣庫的壓力不均致使相互影響的問題。而且性能監控也能精準到庫級別。

結語

以上這些只是一個開始, 後面還有不少功能須要完善,下面是近期策劃的一些功能,其中有些已經在後版中開發完成。 隨着功能的不斷迭代,咱們會打造一個更加完美的私有云平臺。

MySQL 到底能不能放到 Docker 裏跑?數據庫私有云平臺的上線對同程 DB 來講,意味着一個時代的結束,也意味着一個時代的開始。結束的是傳統運維低效、高成本的運維時代,開始的是一個低成本、高效率、高保障的運維時代。咱們相信將來會更美好!

相關文章
相關標籤/搜索