對於無狀態應用程式服務,容器是乙個完美的 DevOps 解決方案。 但是,對於具有持久狀態的資料庫服務來說,事情就不是那麼簡單了。 生產環境是否應該將資料庫放在容器中仍然是乙個有爭議的問題。
從開發人員的角度來看,我非常喜歡 docker,並相信容器可能是未來部署和運維軟體的標準方式。 但從DBA的角度來看,我是這麼認為的目前,將生產資料庫放入 docker k8s 中仍然是乙個壞主意。
docker解決了什麼問題?
讓我們先看看 docker 是如何描述自己的。
docker 用來描述自己的詞包括:輕量化、標準化、便攜化、節約成本、高效化、自動化、整合化、高效運維。這沒有錯,從整體意義上講,Docker 確實使開發和操作更容易。 因此,許多公司都渴望將其軟體和服務容器化。 但有時這種熱情會走向另乙個極端:將所有軟體服務容器化,甚至生產環境的資料庫
容器最初是用於無 國籍,從邏輯上講,容器中的應用程式生成的臨時資料也是容器的一部分。 從容器中建立服務,並在完成後將其銷毀。 這些應用程式本身是沒有狀態的,狀態通常儲存在容器外部的資料庫中,這是經典的架構和用法,也是容器的設計理念。
但是,當使用者也想將資料庫本身放在容器中時,情況就不同了:資料庫是有狀態的為了保持此狀態並在容器停止時不破壞容器,資料庫容器需要在容器中開乙個洞,以與底層作業系統上的資料卷連線。 這樣的容器不再是可以隨意建立、銷毀、運輸和轉移的物件,而是繫結到底層環境的物件。 因此,將容器用於傳統應用程式的許多優勢不再適用於資料庫容器。
可靠性
讓軟體執行與讓它可靠地工作不是一回事。 資料庫是資訊系統的核心,在大多數場景下,它屬於危急應用程式,關鍵應用程式可以從字面上解釋,也就是說,如果出現問題,應用程式將是致命的。 這符合我們的日常經驗:如果wordexcel ppt等辦公軟體崩潰,被迫重啟,沒什麼大不了的;但是,如果正在編輯的文件丟失、骯髒或凌亂,那將是一場真正的災難。 資料庫也是如此,對於很多公司,尤其是網際網絡公司來說,如果資料庫被刪除,沒有可用的備份,基本上可以宣布關閉。
可靠性是資料庫最重要的屬性。 可靠性是指系統在逆境(硬體故障、軟體故障、人為錯誤)下正常執行(正確執行並達到所需效能水平)的能力。 可靠性意味著容錯和彈性,這是其一安全屬性,不像效能和可維護性活動屬性直觀且可衡量。 它只能通過長時間的正常執行或單次故障來證明。 很多人在平時往往會忽略安全屬性,只有在生病、車禍或被搶劫後才會後悔。 安全生產比泰山更重要,資料庫被刪除,被打亂,被帶出倉庫後捶胸頓足毫無意義。
如果你回頭看,docker 不包含對資料庫至關重要的“可靠”屬性。
可靠性和社群知識的證明
如前所述,沒有很好的方法來衡量可靠性。 只有通過長期的正確操作,我們才能逐漸建立對系統可靠性的信心。 在裸機上部署資料庫自古以來就是一種實踐,經過數十年的持續工作,它已經證明了自己。 Docker 已經徹底改變了 DevOps,但只有五年的歷史仍然太年輕,太簡單,無法證明可靠性。 對於乙個值得您付出生命的生產資料庫來說,這還不夠因為沒有足夠的豚鼠去礦山
提高可靠性最重要的是從失敗中吸取教訓。 故障是經驗的寶貴資產:它們將未知問題轉化為已知問題,是操作知識的表達。 社群的大部分失敗經歷都是基於裸機部署的假設幾十年來,各種故障都被踩到了。 如果遇到一些問題,很可能別人已經踩到了坑里,可以更容易地處理和解決。 如果將“docker”關鍵字新增到同一失敗中,則會發現有用的資訊要少得多。 這也意味著當疑難雜症當它出現時,成功救援和恢復資料的可能性較低,並且處理緊急故障需要更長的時間。
微妙的現實是,公司和個人往往不願意在沒有特殊原因的情況下分享他們的失敗經歷。 失敗可能會損害企業的聲譽:它們可能會暴露敏感資訊,或者暴露企業和團隊的垃圾程度。 另一方面,故障經歷幾乎都是真金白銀和學費的損失,這是運維人員的核心價值,所以關於故障的公開資訊並不多。
其他故障點
開發關心功能,而運維關心bug。 與裸機部署相比,將資料庫放入 Docker 並不能降低硬體故障、軟體錯誤或人為錯誤的可能性。 裸機會出現硬體故障,docker 也不會少。 軟體缺陷主要是應用程式錯誤,它們不會通過使用容器來減少,人為錯誤也是如此。 相反,docker 的引入是因為引入了額外的元件、額外的複雜性和額外的故障點,導致整體系統可靠性降低
舉個簡單的例子,如果dockerd守護程序崩潰,資料庫程序將停止,我該怎麼辦。 雖然這種事情發生的概率不高,但它們是在裸露的金屬上它根本不會發生
此外,附加元件引入的故障點可能不超過乙個:docker 不僅僅是 docker 本身的問題。 當故障發生時,可能是Docker單獨導致的問題,Docker與資料庫的互動,或者Docker與作業系統、編排系統、虛擬機器、網路或磁碟的互動。 有關詳細資訊,請參閱 GitHubcom/docker-library/postgres/issues?q=。
正如《從降本增笑到降本增效》中所說。智力難以在空間上積累- 團隊的智力往往取決於最資深的靈魂人物的水平和他們溝通的成本。 當資料庫出現問題時,需要資料庫專家來解決當容器出現問題時,您需要容器專家來檢視問題;然而,當你把乙個資料庫放到Kubernetes中時,很難單獨積累乙個資料庫專家和乙個k8s專家的智力頻寬——你需要乙個雙重專家來解決這個問題。 而且,與單獨的資料庫專家相比,同時精通這兩者的軟體肯定要少得多。
此外,他的蜂蜜,我的砒霜。 某些 docker 功能在某些環境中可能會成為錯誤。
隔離
Docker 提供程序級隔離,這通常是應用程式的良好屬性。 應用程式看不到其他程序,自然不會出現許多互動引起的問題,這反過來又提高了系統的可靠性。 但是,對於資料庫來說,隔離並不一定是一件好事。
乙個微妙的真實世界的例子是的在同一資料目錄上啟動兩個 PostgreSQL 例項,或者在主機和容器中同時啟動兩個資料庫例項。 在裸機上第二次啟動嘗試失敗,因為 postgresql 知道存在另乙個例項並拒絕啟動;但是在使用 docker 的情況下,因為它隔離,則第二個例項不知道主機或其他資料庫容器中的另乙個例項。 如果沒有適當的隔離機制(例如,通過主機埠互斥鎖、pid 檔案互斥鎖),則在同一資料目錄上執行的兩個資料庫程序可能會弄亂資料檔案。
資料庫是否需要隔離?當然需要,但不是這種隔離。 資料庫的效能很重要,因此它通常以獨佔方式部署在物理機上。 除了資料庫程序和必要的工具外,不會有其他應用程式。 即使放置在容器中,它也傾向於以物理機的獨佔繫結模式執行。 因此,docker 提供的隔離對於此資料庫部署方案沒有多大意義;但是,對於雲資料庫供應商來說,這是一項實用的功能,對於多租戶超賣非常有用。
工具
資料庫需要工具進行維護,包括各種運維指令碼、部署、備份、歸檔、故障轉移、版本公升級、外掛程式安裝、連線池、效能分析、監控、調優、巡檢、修復等。 這些工具中的大多數也是為裸機部署而設計的。 這些工具(如資料庫)需要仔細徹底的測試。 保持某物執行與確信它會長時間穩定正確地執行是完全不同的可靠性水平。
乙個簡單的例子是外掛程式和包管理,PostgreSQL提供了許多有用的外掛程式,例如PostGIS。 如果要為資料庫安裝外掛程式,只需執行 yum install,然後在裸機上建立擴充套件 postgis 命令即可。 但是,如果是在 Docker 中,根據 Docker 的實用原理,使用者需要在映象級別進行此更改,否則下次重啟容器時擴充套件會消失。 因此,需要修改 dockerfile,重新構建新映象並推送到伺服器,最後重新啟動資料庫容器,無疑,要麻煩得多。
包管理是作業系統發行版的核心問題。 然而,Docker 已經搞砸了這一切,例如,許多 PostgreSQL 不再將二進位檔案發布為 rpm deb 包,而是將它們作為擴充套件的 Postgres docker 映像分發。 這立即提出了乙個明顯的問題,即如果我想同時使用兩個、三個或一百個左右的 PG 生態系統擴充套件,如何將這些碎片化的影象組合在一起與可靠的作業系統包管理相比,構建 docker 映象總是需要更多的時間和精力才能正常工作。
另乙個例子是傳統裸機部署模型中的監視機器指標是資料庫指標的重要組成部分。 容器中的監視和裸機上的監視之間存在許多細微的差異。 如果不小心,你可能會掉進坑里。 例如,在裸機上,CPU 模式的總和將始終為 100%,但此假設在容器中可能並不總是成立。 例如,依賴於 proc 檔案系統的監視器在容器中的指標可能與裸機上的指標完全不同。 雖然這類問題最終是可以解決的(例如,將 proc 檔案系統掛載到容器中),但與簡單的解決方案相比,沒有人喜歡複雜而醜陋的解決方法。
類似的問題包括一些故障檢測工具和常用的系統命令,雖然理論上可以直接在主機上執行,但誰能保證容器中的結果和裸機上的結果具有相同的意義呢?更棘手的是,在緊急排查時,一些需要臨時安裝使用的工具在容器中不可用,外網不可用。
如果您使用 docker 作為虛擬機器,許多工具可能會正常工作,但這將失去使用 docker 的大部分意義,而只是將其用作另乙個包管理器。 有人覺得docker通過標準化部署方式增加了系統的可靠性,因為環境更加規範可控。 這是不可否認的。 在我看來,雖然標準化的部署方法很好,但如果操作和管理資料庫的人自己知道如何配置資料庫環境,那麼在shell指令碼中編寫環境初始化命令和在dockerfile中編寫環境初始化命令之間沒有本質的區別。
可維護性
軟體的大部分開銷不是在初始開發階段,而是在持續維護階段,包括修復錯誤、保持系統正常執行、處理故障、版本公升級、償還技術債務、新增新功能等。 可維護性對於運維人員的生活質量非常重要。
我應該說可維護性是 Docker 最討人喜歡的地方:基礎設施即程式碼。 可以認為,docker最大的價值在於,它可以將軟體的運維經驗沉澱成可復用的**,並以簡單的方式積累起來,而不是安裝散落在各個角落的設定文件。
Docker 在這方面做得很好,特別是對於邏輯經常變化的無狀態應用程式。 Docker 和 K8S 允許使用者輕鬆部署、完成擴容、縮容、發布、滾動公升級等,讓 Dev 也可以做 Ops 的工作,OPS 也可以做 DBA 的工作。
環境配置
如果說 docker 最大的優勢是什麼,那大概就是環境配置的標準化了。 標準化環境有助於交付更改、傳達問題和重現錯誤。 使用二進位映像(實質上是具體化的 dockerfile 安裝指令碼)比執行安裝指令碼更快、更易於管理。 有些編譯很複雜,擴充套件的依賴項不必每次都重建,這些都是很棒的功能。
遺憾的是,資料庫不像業務應用程式那樣頻繁地來來去去,並且建立新例項或交付環境本身是乙個非常不頻繁的操作。 同時,DBA通常會積累各種安裝和配置維護指令碼,一鍵配置環境並不比Docker慢多少。 所以,docker在環境配置上的優勢就沒那麼明顯了,只能說好好h**e。 當然,當你沒有專職的DBA時,使用Docker映象可能比盲目折騰要好,因為至少在映象中沉澱了一些運維經驗。
通常,資料庫在初始化後執行數月或數年的情況並不少見。 資料庫管理的主要部分不是新建例項和交付環境,而是day2操作,這是日常運維的一部分。 不幸的是,docker 在這方面沒有任何優勢,而且會造成很多額外的麻煩。
day2 operation
Docker 確實大大簡化了無狀態應用程式的日常維護,例如建立和銷毀、版本公升級、擴充套件等,但同樣的結論可以擴充套件到資料庫嗎?
資料庫容器不能像應用程式容器那樣被任意銷毀、建立和遷移。 因此,docker 並沒有改善日常的資料庫操作體驗,而是像 Ansible 這樣的工具真正有幫助。 對於日常運維,很多操作都需要使用 docker exec 將指令碼透明地傳輸到容器中執行。 它仍然是在底部執行的相同指令碼,但 docker-exec 用於執行它,並帶有額外的打包層,這有點放屁。
此外,許多命令列工具與 docker 一起使用非常尷尬。 例如,docker exec 混合了 stderr 和 stdout,使許多依賴於管道的命令不起作用。 以 PostgreSQL 為例,在裸機部署模式下,一些日常的 ETL 任務可以通過一行 bash 輕鬆處理:
psql -c 'copy tbl to stdout' |\psql -c 'copy tdb from stdin'但是,如果主機上沒有適當的客戶端二進位檔案,則只能在 docker 容器中使用該二進位檔案
docker exec -it srcpg gosu postgres bash -c "psql -c \"copy tbl to stdout\" 2>/dev/null" |\docker exec -i dstpg gosu postgres psql -c 'copy tbl from stdin;'當您想在容器中對資料庫進行物理備份時,乙個簡單的命令現在需要大量額外的包裝: docker set gosu set bash set pg basebackup:
docker exec -i postgres_pg_1 gosu postgres bash -c 'pg_basebackup -xf -ft -c fast -d - 2>/dev/null' | tar -xc /tmp/backup/basebackup如果客戶端應用 psql|pg_basebackup|PG dump 也可以通過在主機上安裝相應版本的客戶端工具來繞過這個問題,所以伺服器端應用真的是無法解決的。 您不能每次不斷公升級容器中的資料庫軟體版本時都公升級主機上的伺服器端二進位版本,對嗎?
docker 喜歡談論的另乙個例子是軟體版本公升級例如,如果使用 docker 公升級資料庫次要版本,則只需更改 docker 檔案中的版本號,重新生成映像,然後重新啟動資料庫容器即可。 沒錯,至少對於無狀態應用程式是這樣。 但是,當需要公升級資料庫的就地主要版本時,問題就出現了,使用者需要同時修改資料庫狀態。 在裸機上使用單個 bash 命令可以解決的問題可能會在 docker 下像這樣結束:
如果資料庫容器不能像應用伺服器那樣快速排程和伸縮,在初始配置、日常運維、緊急故障處理等方面都不能帶來比普通指令碼更多的便利,我們為什麼要把生產環境中的資料庫塞進容器裡呢?
Docker 和 K8S 的一大優點是它易於擴充套件,至少對於無狀態應用程式而言是這樣:只需按一下按鈕,就可以調出一些新容器,而要排程到哪個節點並不重要。 但是,作為有狀態應用程式,不能像普通應用伺服器那樣建立、銷毀和水平擴充套件資料庫。 例如,如果建立乙個新的從屬伺服器,則必須再次從主伺服器中提取基本備份,即使您使用的是容器。 在生產環境中,往往有幾TB的資料庫,建立副本需要乙個小時,還需要人工干預和檢查,並逐漸預熱快取以線上承載流量。 相比之下,在相同的初始作業系統環境中,執行現成的從屬指令碼和執行 docker run 之間的本質區別是什麼 - 時間花在拖動從屬庫上。
使用 docker 儲存生產資料庫的乙個尷尬方面是資料庫是有狀態的,並且需要額外的步驟來建立該狀態。 一般來說,設定新的 PostgreSQL slave 的過程是通過 pg baseback 建立資料目錄的本地副本,然後在本地資料目錄上啟動 PostMaster 程序。 但是,容器繫結到程序,並在程序退出容器後停止。 因此,為了在 Docker 中擴容乙個新的 slave,需要啟動 pg baseback 容器來拉取資料目錄,然後在同乙個資料卷上啟動兩個 postgres 容器您需要在容器建立過程中指定複製目標,並等待幾個小時以完成複製要麼在 postgres 容器中使用 pg basebackup 來每天替換資料目錄。 這兩種選擇既不優雅也不簡潔。 由於容器的這種程序隔離抽象,因此存在乙個充滿狀態的多程序、多工、多例項協作應用程式,例如資料庫抽象洩漏,很難優雅地覆蓋這些場景。 當然,為了解決這類問題,可以進行許多權衡,但代價是很多額外的複雜性歸根結底,是系統的可維護性受到了傷害。
總的來說,Docker可以在一定程度上提高系統的可維護性,比如簡化新例項的建立,但相比之下,它帶來的新麻煩就顯得蒼白無力了。
效能
效能也是人們經常關注的乙個維度。 當然,從效能的角度來看,資料庫部署的基本原則是,離硬體越近越好,額外的隔離和抽象對資料庫的效能是有害的:更多的隔離意味著更多的開銷,即使它只是核心堆疊中的額外副本。 對於效能驅動的場景,一些資料庫選擇繞過作業系統的頁面管理機制,直接操作磁碟,而一些資料庫甚至使用FPGA甚至GPU來加速查詢處理。
實事求是地說,作為乙個輕量級的容器,Docker並沒有明顯的效能損失,通常不超過10%。 但毫無疑問將資料庫放入 docker 只會使效能變差,而不是更好
總結
容器技術和編排技術對於運維來說是非常有價值的東西,它實際上彌合了軟體和服務之間的鴻溝,其願景是將運維的體驗和能力模組化。 容器技術將成為未來的包管理方式,編排技術將進一步發展成為“資料中心分布式集群作業系統”,成為所有軟體的底層基礎設施執行時。 當越來越多的坑被踩到時,人們可以自信而大膽地將所有應用程式(有狀態和無狀態)放入容器中執行。 但現在,至少對於資料庫來說,它只是乙個美麗的願景和乙個薄弱的選擇。
同樣,上述討論僅限於:生產環境資料庫。對於開發測試,我也支援使用 docker,儘管存在基於流浪者的虛擬機器沙箱——畢竟,並非所有開發人員都知道如何配置本地測試資料庫環境,使用 docker 交付環境顯然比一堆手冊簡單得多。 適用於生產環境無 國籍對於應用程式來說,Docker 也是乙個不錯的選擇,甚至對於具有派生狀態的不太重要的派生資料系統(例如 Redis 快取)。 但是,對於生產環境中的核心關係型資料庫來說,如果其中的資料真的很重要,那麼在使用 Docker 之前還是需要三思而後行:這樣做有什麼價值?你能堅持不治之症嗎?你能把這個鍋搞砸並攜帶它嗎?
任何技術決策都是一種權衡,比如這裡與Docker的核心權衡犧牲可靠性來換取可維護性。確實有一些場景,資料可靠性不是那麼重要,或者還有其他考慮因素:比如對於雲計算廠商來說,把資料庫放在容器裡,混合超賣是一件好事:容器的隔離、資源利用率高、管理便利性都非常適合這個場景。 在這種情況下,將資料庫放入 docker 中,也許對他們來說利大於弊。 但對於更多場景,可靠性往往是最高優先順序的屬性,為了可維護性而犧牲可靠性通常不是乙個理想的選擇。 更重要的是,很難說使用 Docker 管理資料庫的工作有多容易:為了一次性的安裝和部署便利而犧牲長期的日常操作可維護性並不是乙個好主意。
綜上所述,將生產環境的資料庫放在容器中確實不是乙個明智的選擇。
參考閱讀
原文:把資料庫放進docker是個好主意嗎?
docker 風景已經不復存在了嗎?Gitlab高階工程師:以前很喜歡,現在很討厭!
作者丨馮若航**丨***非法嘉豐(ID:addvon) dbaplus社群歡迎技術人員投稿,投稿郵箱:editor@dbapluscn