使用Docker部署PHP應用的設計方案
1. Docker
Docker的官方定義是:
Docker allows you to package an application with all of its dependencies into a standardized unit for software development.
-- https://www.docker.com/whatisdocker
</blockquote>毫無疑問的是,Docker解決了應用部署上一個巨大的問題:
客戶: 安裝好了,用不了。
發布者:我的機器上沒問題。
如何解決每個應用的依賴在Docker出現之前是個頭疼的問題,現在僅僅通過一次配置,Dockerfile或者image作為最終交付,就能在任何Linux上完美運行了。說起來很簡單的樣子,但在Docker配置過程中,又存在很多值得思考的問題:應用各個組件如何安排?一個Container解決問題還是細化Container?Container之間何如通信?等等。。下面用一個最普遍的WEB應用配置部署來說明這些問題。
NOTE:本文假定讀者對Docker中的一些概念有基本的認識,如果不甚了解,我推薦這篇文章:
https://linux.cn/article-6074-weibo.html
2. LNMP
典型的PHP應用配置方案是LAMP或者LNMP,本文以LNMP為例。
設計方案如下圖(我已經實現并運行成功的案例):
![]()
應用由4個組件組成,分別是Nginx,PHP-FPM(PHP),MySQL以及WWW,4個組件運行在由各自鏡像創建出來的獨立的容器中。其中WWW Container只是一個存儲業務代碼和靜態資源的容器,可以認為是"死"的。
事實上LNMP架構采用上面的設計方式應該是最容易想到的,也是最清晰的,每個組件有相對的獨立性。其中除了WWW容器,其他3個容器都可以直接通過官方鏡像構建出來。
然而網上很多同學并不是這樣做的,不會分的這么細,通常是把Nginx和WWW放到一個容器內,或者干脆全部放到一個容器中。可以學習一下大家的Dockerfile:
https://github.com/search?utf8=?&q=docker-lnmp
細化Container這種設計的優缺點:
容器間的耦合性增大。可以看到PHP-FPM容器和另外三個容器間有耦合關系,MySQL容器最獨立。
</li>雖然耦合性比較大,但這種端口耦合,文件系統耦合關系可以通過增加幾個運行選項解決掉,后面有介紹。
</li>由于容器對整個架構的劃分,使得容器中的內容變得十分獨立和安全。例如,我希望在線上更新WWW中的代碼,只需要進入WWW容器做修改,不會影響到Nginx,PHP-FPM或者是MySQL。
</li>各容器可靈活拆卸更換,比如我想把MySQL換成Mongodb,或者干脆把業務代碼搬個家,不會影響到其他容器(僅僅更改相關配置文件)
</li>由于各容器經由官方的鏡像創建,因此可以隨時花最少的代價使用最新的官方鏡像嘗鮮。
</li>占用空間會比較大,一個簡單的應用要這么做的話,四個鏡像會占用大量的存儲空間。
</li> </ol>2.1 容器間通信問題
細化Container面臨著另一個問題,就是如何進行容器間通信。下面簡要描述一下上圖中的數據流程:
客戶端的http請求達到server的80端口,該端口被映射到Nginx Container的80端口,因此進入Nginx處理。Nginx會分析請求資源,判定是靜態資源還是php腳本,如果是靜態資源,則直接從WWW中取出發回客戶端;如果是腳本程序,則要告訴PHP-FPM到WWW獲取相應腳本,然后通過php-cgi處理。
</li>fast-cgi通過php執行腳本,必要時訪問MySQL存取數據。
</li> </ol>這樣耦合關系就出來了:
Nginx需要連接PHP-FPM開放的9000端口,需要訪問WWW中的文件系統。
</li>PHP-FPM也需要訪問WWW中的文件系統,還要訪問MySQL的3306端口。
</li> </ol>2.2 解決問題
可以看出有兩類耦合關系:端口和文件系統。
對于端口耦合,docker是通過--link選項解決的;對于文件系統耦合,docker是通過--volumes-from選項解決的。
解決第一個耦合關系:
$ sudo docker run -p 80:80 -p 443:443 # 主機端口映射到容器 --volume-from WWW_CONTAINER_NAME # 把WWW容器VOLUME過的文件夾掛載到將啟動的容器上 --link PHP_FPM_CONTAINER_NAME:fpmservice # 冒號前是正在運行的FPM容器名稱,后面是別名,別名會作為hostname在將啟動的容器內可見 -d # detach NGINX_IMAGE # 鏡像名解決第二個耦合關系:
$ sudo docker run --volume-from WWW_CONTAINER_NAME --link MYSQL_CONTAINER_NAME:mysql -d PHP_FPM_IMAGE參考文檔:https://docs.docker.com/reference/run/
因此容器啟動的先后順序就出來了:
MySQL Container
</li>WWW Container (由于沒有任何服務運行,容器run后會立即exit,可以使用 tail -f 等block命令使容器保持運行不退出)
</li>PHP-FPM Container
</li>Nginx Container
</li> </ol>其中1和2可以對換。
3. 總結
利用Docker部署Web應用可以帶來很多便利,在宏觀上實現應用組件化,為實現分布式系統奠定了基礎。
可以看到實際上在Docker容器間共享數據是很方便的,搞清楚各容器的依賴關系就不難解決。
來自:http://my.oschina.net/ybusad/blog/499013