為什么使用單進程容器
使用容器時,要盡量使用單進程容器,所謂單進程容器,是指在容器運行時,只有一個工作進程。
如果需要存在多個進程協作的時候,要部署為兩個容器,比如 PHP 一個容器,MySQL 一個容器,而不要在一個容器中運行這兩者。
因為,Docker本身就是一個非常好的守護進程,它可以完美地管理一個進程,但是如果一個容器中存在多個進程時,你就需要自己維護兩個進程的運行狀態,比如使用 supervisord ,但這就大大增加了容器維護的難度和不穩定性。
比如在一個容器中同時運行 PHP 和 MySQL,那么如果PHP異常退出了,容器該不該連同MySQL一起退出?如果不退出,而是不斷重啟PHP,那么在容器之外,比如運行 docker ps 是無法了解到PHP運行狀態的。
所以,使用docker,就要習慣於單進程容器的方式,既簡單,又穩健。
為什么使用無狀態容器
所謂狀態,是指程序在執行過程中生成的中間數據,而無狀態容器,是指容器在運行時,不在容器中保存任何數據,而將數據統一保存在容器外部,比如數據庫中。
因為有狀態的容器異常重啟就會造成數據丟失,也無法多副本部署,無法實現負載均衡。
比如PHP的Session數據默認存儲在磁盤上,比如 /tmp 目錄,而多副本負載均衡時,多個PHP容器的目錄是彼此隔離的。比如存在兩個副本A和B,用戶第一次請求時候,流量被轉發到A,並生成了SESSION,而第二次請求時,流量可能被負載均衡器轉發到B上,而B是沒有SESSION數據的,所以就會造成會話超時等BUG。
如果采用主機卷的方式,多個容器掛載同一個主機目錄,就可以共享SESSION數據,但是如果多主機負載均衡場景,就需要將SESSION存儲於外部數據庫或Redis中了。
除了文件,還有內存數據,比如Node.js項目中使用了全局變量暫存數據,那么這個容器也是有狀態的,也會出現類似BUG,所以要使用無狀態容器。
Docker不適合部署數據庫的幾大原因
個人對部分問題是否真的存在存疑,僅做參考
1、數據安全問題
不要將數據儲存在容器中,這也是 Docker 官方容器使用技巧中的一條。容器隨時可以停止、或者刪除。當容器被rm掉,容器里的數據將會丟失。為了避免數據丟失,用戶可以使用數據卷掛載來存儲數據。
但是容器的 Volumes 設計是圍繞 Union FS 鏡像層提供持久存儲,數據安全缺乏保證。如果容器突然崩潰,數據庫未正常關閉,可能會損壞數據。
2、性能問題
大家都知道,MySQL 屬於關系型數據庫,對IO要求較高。當一台物理機跑多個時,IO就會累加,導致IO瓶頸,大大降低 MySQL 的讀寫性能。
在一次Docker應用的十大難點專場上,某國有銀行的一位架構師也曾提出過:“數據庫的性能瓶頸一般出現在IO上面,如果按 Docker 的思路,那么多個docker最終IO請求又會出現在存儲上面。現在互聯網的數據庫多是share nothing的架構,可能這也是不考慮遷移到 Docker 的一個因素吧”。
針對性能問題有些同學可能也有相對應的方案來解決:
(1)數據庫程序與數據分離
如果使用Docker 跑 MySQL,數據庫程序與數據需要進行分離,將數據存放到共享存儲,程序放到容器里。如果容器有異常或 MySQL 服務異常,自動啟動一個全新的容器。另外,建議不要把數據存放到宿主機里,宿主機和容器共享卷組,對宿主機損壞的影響比較大。
(2)跑輕量級或分布式數據庫
Docker 里部署輕量級或分布式數據庫,Docker 本身就推薦服務掛掉,自動啟動新容器,而不是繼續重啟容器服務。
(3)合理布局應用
對於IO要求比較高的應用或者服務,將數據庫部署在物理機或者KVM中比較合適。目前TX雲的TDSQL和阿里的Oceanbase都是直接部署在物理機器,而非Docker 。
3、網絡問題
要理解 Docker 網絡,您必須對網絡虛擬化有深入的了解。也必須准備應付好意外情況。
網絡對於數據庫復制是至關重要的,其中需要主從數據庫間 24/7 的穩定連接。未解決的 Docker 網絡問題在1.9版本依然沒有得到解決。
4、狀態
在 Docker 中水平伸縮只能用於無狀態計算服務,而不是數據庫。
Docker 快速擴展的一個重要特征就是無狀態,具有數據狀態的都不適合直接放在 Docker 里面,如果 Docker 中安裝數據庫,存儲服務需要單獨提供。和1類似。
目前,TX雲的TDSQL(金融分布式數據庫)和阿里雲的Oceanbase(分布式數據庫系統)都直接運行中在物理機器上,並非使用便於管理的 Docker 上。
5、資源隔離
資源隔離方面,Docker 確實不如虛擬機KVM,Docker是利用Cgroup實現資源限制的,只能限制資源消耗的最大值,而不能隔絕其他程序占用自己的資源。如果其他應用過渡占用物理機資源,將會影響容器里 MySQL 的讀寫效率。
我們沒有看到任何針對數據庫的隔離功能,那為什么我們應該把它放在容器中呢?