在我們公司ChinaNetCloud,見(jiàn) 過(guò)多種不同類型的網(wǎng)站和系統(tǒng),有好也有差。其中有些系統(tǒng)擁有良好的服務(wù)器/網(wǎng)絡(luò)架構(gòu),并且進(jìn)行了合理的調(diào)整和監(jiān)控 ;然而一般的系統(tǒng)都會(huì)有安全和性能上的 問(wèn)題,不能良好運(yùn)行,也無(wú)法變得更流行。
在中國(guó), 開(kāi)源的LAMP棧是最流行的網(wǎng)絡(luò)架構(gòu),它使用PHP開(kāi)發(fā),運(yùn)行在Apache服務(wù)器上,以MySQL作為數(shù)據(jù)庫(kù),所有這些都運(yùn)行在Linux上。它是個(gè)可靠的平臺(tái),運(yùn)行良好,是現(xiàn)在全球最 流行的Internet系統(tǒng)架構(gòu)。然而,我們很難對(duì)其規(guī)模進(jìn)行正確的擴(kuò)展并保持安全性,因?yàn)槊總€(gè)應(yīng)用層都有其自身的問(wèn)題、缺陷和最佳實(shí)踐。我們的工作就是 幫助企業(yè)用最低的操作成本來(lái)創(chuàng)建并運(yùn)行高性能的、可伸縮的、安全的系統(tǒng),因此對(duì)于這類問(wèn)題我們有很豐富的經(jīng)驗(yàn)。
當(dāng)前的實(shí)際情況是,很多網(wǎng)站都是由開(kāi)發(fā)人員 快速而廉價(jià)地創(chuàng)建,通常沒(méi)有任何IT人員或者經(jīng)理,只是由程序員來(lái)管理系統(tǒng)。造成的結(jié)果是,雖然花費(fèi)很低的成本網(wǎng)站就可以開(kāi)始運(yùn)行,但是當(dāng)擁有大量用戶、 需要擴(kuò)展規(guī)模的時(shí)候,通常就會(huì)面臨真正的問(wèn)題。畢竟,中國(guó)擁有三億八千萬(wàn)的Internet用戶,如果其中的0.01%訪問(wèn)這個(gè)站點(diǎn),就很容易引發(fā)25 萬(wàn)~50萬(wàn)的頁(yè)面訪問(wèn)量。這些問(wèn)題在各個(gè)級(jí)別上都會(huì)產(chǎn)生,下面總結(jié)的規(guī)則是對(duì)最一般的問(wèn)題進(jìn)行概述,并且說(shuō)明為什么這些規(guī)則如此重要,以及最好采用什么方 法來(lái)修正它們。遵循這些建議的站點(diǎn)會(huì)提高它的可伸縮性、安全性以及操作上的穩(wěn)定性。
使用合適的會(huì)話管理
第 一個(gè)想到的擴(kuò)展系統(tǒng)的方法就是添加更多硬件。例如,使用兩臺(tái)服務(wù)器而不是一臺(tái)。這聽(tīng)著合理,但會(huì)產(chǎn)生潛在問(wèn)題:會(huì)話管理。這對(duì)Java程序來(lái)說(shuō)是很嚴(yán)重的問(wèn)題,在PHP中也會(huì)產(chǎn)生可延展性問(wèn)題, 對(duì)于數(shù)據(jù)庫(kù)的負(fù)載尤其如此。
會(huì)話被定義為單獨(dú)的最終用戶登錄或者連接一 段時(shí)間,其中通常會(huì)包含多個(gè)TCP/IP的HTTP連接、幾個(gè)Web頁(yè)面,通常還包括幾十個(gè)甚至上百個(gè)頁(yè)面元素,如框架、菜單、Ajax更新等。所有這些 HTTP請(qǐng)求都需要知道用戶是誰(shuí),才能滿足安全的要求,并向用戶傳送適當(dāng)?shù)膬?nèi)容,因?yàn)檫@些都是會(huì)話的組成部分。通常每個(gè)會(huì)話都會(huì)包括相互關(guān)聯(lián)的會(huì)話數(shù)據(jù), 如用戶名、用戶ID、歷史、購(gòu)物車、統(tǒng)計(jì)資料等等信息。
問(wèn)題在 于,在有兩臺(tái)Web服務(wù)器和多個(gè) HTTP連接的情況下,用戶流量會(huì)在兩臺(tái)服務(wù)器之間分配和移動(dòng),服務(wù)器很難知道用戶是誰(shuí),并對(duì)所有數(shù)據(jù)進(jìn)行跟蹤,因?yàn)槊總€(gè)頁(yè)面或者頁(yè)面的組成部分都可能來(lái) 自不同的服務(wù)器。在PHP中,通常是這樣解決的,在第一次連接或登錄的時(shí)候就創(chuàng)建一個(gè)會(huì)話ID并將其放在Cookie中,然后這個(gè)Cookie會(huì)和每個(gè) HTTP請(qǐng)求一起發(fā)送。
這樣做帶來(lái)一個(gè)問(wèn)題,接下來(lái)每段PHP腳本 都需要基于ID來(lái)查找會(huì)話數(shù)據(jù)。由于PHP無(wú)法在執(zhí)行過(guò)程之間保持狀態(tài)(這與Java不同),這個(gè)會(huì)話數(shù)據(jù)需要存儲(chǔ)在某個(gè)地方,通常是在數(shù)據(jù)庫(kù)中。但是, 如果復(fù)雜的頁(yè)面需要在每個(gè)頁(yè)面載入過(guò)程中對(duì)其進(jìn)行十次查找(這是經(jīng)常要做的),那就意味著每個(gè)頁(yè)面都要執(zhí)行10次SQL查詢,這會(huì)導(dǎo)致數(shù)據(jù)庫(kù)上很大的負(fù) 載。
在前面所舉的中國(guó) Internet用戶 0.01%的例子中,可能很容易在每秒內(nèi)僅僅為了管理會(huì)話就生成上百個(gè)查詢。解決方法是一直使用位于Cookie中的會(huì)話ID,并且使用像 Memcached之類的服務(wù)來(lái)緩存會(huì)話數(shù)據(jù)以獲得高性能。
還要注 意其中存在安全性的問(wèn)題,因?yàn)楹诳涂?以偽造另一個(gè)用戶的會(huì)話ID,這是很容易找到或看到的,特別是在公用的Wi-Fi中。解決方法是對(duì)會(huì)話ID進(jìn)行恰當(dāng)?shù)募用芑蛘吆灻?,并將其與時(shí)間區(qū)間、 IP地址以及其他關(guān)鍵信息 像瀏覽器或者其他細(xì)節(jié)相綁定。在Internet上有很多不錯(cuò)的關(guān)于良好的會(huì)話管理的例子,你可以根據(jù)需要找到最適合的。
?
總是要考慮安全性
盡管編寫像防止SQL注入和登錄安全之類的 代碼涉及很多安全問(wèn)題,但不幸的是,幾乎沒(méi)有人考慮過(guò)安全性,而那些考慮到的人也沒(méi)有對(duì)其進(jìn)行很好地理解。而本文要關(guān)注的是操作性的系統(tǒng)安全。對(duì)于這類安 全,我們的焦點(diǎn)集中在三個(gè)安全領(lǐng)域:防火墻、運(yùn)行的用戶以及文件訪問(wèn)權(quán)限。
除了配置專門的硬件防火墻(像Cisco的 ASA)之外,所有服務(wù)器都還應(yīng)該運(yùn)行像Iptables之類的防火墻,它會(huì)保護(hù)服務(wù)器免受其他威脅和攻擊。這些威脅和攻擊可能來(lái)自公共的 Internet、其他服務(wù)器或本地服務(wù)器,也包括使用VPN或者SSH通道的開(kāi)發(fā)和操作人員。我們僅對(duì)指定的IP開(kāi)放確實(shí)需要的端口。Iptables 可能會(huì)很復(fù)雜,但是有很多不錯(cuò)的模板,我們通??梢允褂盟鼈儊?lái)幫助客戶創(chuàng)建Iptables。例如,默認(rèn)的RedHat或者CentOS防火墻的配置說(shuō)明 只有10行,顯然并不實(shí)用。我們最佳實(shí)踐的Iptables配置大概有5頁(yè),這其中包含了Linux所能提供的最高級(jí)的安全防范。
所有公用的服務(wù),都應(yīng)該運(yùn)行在專門的用戶 下,如Apache。切記永遠(yuǎn)都不要使用Root用戶運(yùn)行,因?yàn)檫@會(huì)讓任何闖入到Apache的用戶接管整個(gè)服務(wù)器。如果Apache只是運(yùn)行在 Apache用戶下或者運(yùn)行在Nobody下,那么闖入Apache就不是一件容易的事情了。
Web服務(wù)器運(yùn)行或者服務(wù)的文件 (像.php和.html文件)對(duì)于Web服務(wù)器的用戶應(yīng)該是不可寫的。這意味著Apache或者Nginx用戶不應(yīng)該擁有Web目錄的寫權(quán)限。有很多方 法都可以做到這一點(diǎn),而最簡(jiǎn)單的就是將這些文件為其他用戶所有,然后讓Apache/Nginx等用戶歸屬于能夠使用640權(quán)限讀取文件的組中。這會(huì)防范 幾乎所有的黑客和針對(duì)頁(yè)面的攻擊。
此 外,永遠(yuǎn)不要使用Ftp來(lái)上傳文件,特別 是在公用的Wi-Fi環(huán)境中,因?yàn)樵谄渲泻诳秃苋菀妆I取用戶名和密碼。取而代之的是使用Sftp會(huì)更加安全。另外,每個(gè)雇員都應(yīng)該擁有自己的用戶ID和隨 機(jī)密碼。
?
使用標(biāo)準(zhǔn)的路徑和安裝配置
一個(gè)令人討厭的部署問(wèn)題是,開(kāi)發(fā)者很少考慮 他們的軟件會(huì)被部署到生產(chǎn)Web服務(wù)器的什么位置,以及如何部署。我們看到過(guò)許多大型的系統(tǒng)將它們的PHP代碼部署在/home/xiaofeng或者 /web/code路徑下。事實(shí)上,這兩個(gè)路徑都是非常不標(biāo)準(zhǔn)的,并且會(huì)帶來(lái)操作和安全性的問(wèn)題。當(dāng)這些系統(tǒng)從開(kāi)發(fā)環(huán)境轉(zhuǎn)移到測(cè)試環(huán)境再到生產(chǎn)環(huán)境中時(shí), 因?yàn)槊總€(gè)安裝配置都是非標(biāo)準(zhǔn)的,所以經(jīng)常會(huì)出現(xiàn)問(wèn)題,這時(shí)就需要開(kāi)發(fā)者調(diào)整才能夠正常工作。
你應(yīng)該總是使用標(biāo)準(zhǔn)的安裝包和二進(jìn)制文件來(lái) 安裝像Apache之類的服務(wù)器。不要從源代碼編譯或者安裝Tarball,因?yàn)檫@會(huì)導(dǎo)致長(zhǎng)期穩(wěn)定性和管理上的問(wèn)題,另外在服務(wù)器上安裝多個(gè)不同的版本也 會(huì)造成混淆。
Web 站點(diǎn)應(yīng)該總是在指定的平臺(tái)和 Linux發(fā)布的標(biāo)準(zhǔn)路徑下進(jìn)行測(cè)試和部署 ,像 RedHat 或者CentOS下的/var/www/html路徑。這有助于對(duì)系統(tǒng)進(jìn)行有效的權(quán)限管 理、備份、配置、監(jiān)控以及其他操作。
Web 服務(wù)器的日志應(yīng)該存放在/var /logs或者/var/logs/app_name下,而不應(yīng)該位于主代碼區(qū)域。這樣做的原因不僅僅是因?yàn)檫@些標(biāo)準(zhǔn)的路徑很重要,更應(yīng)該關(guān)注的是,恰當(dāng) 地配置服務(wù)器會(huì)將/var配置為分離的文件系統(tǒng)。如果應(yīng)用程序突然寫入了大量日志并占用所有磁盤空間,由于我們做了以上的配置就不會(huì)導(dǎo)致系統(tǒng)崩潰,或者其 他嚴(yán)重的問(wèn)題。如果日志位于其他位置,就可能會(huì)產(chǎn)生問(wèn)題。
總是使用日志
在Web系統(tǒng)中做多少日志都不為過(guò)。所有系 統(tǒng)都應(yīng)該將重要的數(shù)據(jù)寫入到日志中,不管是它們自己的日志還是系統(tǒng)的Syslog。Cron的Job以及其他Shell腳本或者C語(yǔ)言的程序,對(duì)日志都有 相應(yīng)標(biāo)準(zhǔn)以及簡(jiǎn)單的函數(shù)。在Shell腳本中,只需要使用 Logger命令就可以實(shí)現(xiàn)日志的寫入。在腳本啟動(dòng)/停止、重要的腳本執(zhí)行以及實(shí)時(shí)數(shù)據(jù)產(chǎn)生的 情況下都要執(zhí)行寫入日志操作。這樣出現(xiàn)問(wèn)題的時(shí)候,查看主要的系統(tǒng)日志就可以很容易地看到發(fā)生了什么。
大型系統(tǒng)經(jīng)常會(huì)使用專門的工具如 Local5來(lái)記錄日志,并配置Syslog或者Syslog-ng來(lái)將其存放在單獨(dú)的文件中,這樣會(huì)更容易使用。需要注意的是,Syslog工具和 Logger(以及任何Syslog調(diào)用)默認(rèn)優(yōu)先使用user.notice,如有必要,你可以對(duì)其進(jìn)行調(diào)整。
一個(gè)好的系統(tǒng)會(huì)對(duì)程序進(jìn)行配置,用來(lái)打開(kāi)或 者關(guān)閉日志,并可以選擇在每模塊或者功能的級(jí)別上應(yīng)用不同級(jí)別的日志。這使得我們可以記錄非常詳細(xì)和強(qiáng)大的日志,用來(lái)分析和調(diào)試在生產(chǎn)操作中所發(fā)生的問(wèn) 題。
使用良好的數(shù)據(jù)庫(kù)設(shè)計(jì)和SQL
在任何系統(tǒng)中,數(shù)據(jù)庫(kù)通常是最大的性能瓶 頸。而影響數(shù)據(jù)庫(kù)性能的最大兩個(gè)問(wèn)題是數(shù)據(jù)庫(kù)設(shè)計(jì)和SQL代碼質(zhì)量。很多系統(tǒng)都擁有良好的或者至少是可用的數(shù)據(jù)庫(kù)設(shè)計(jì),但由于沒(méi)有經(jīng)過(guò)適當(dāng)?shù)男阅軠y(cè) 試,SQL代碼質(zhì)量通常都會(huì)很差。這樣的SQL代碼在開(kāi)發(fā)環(huán)境中可能運(yùn)行很快,因?yàn)槠渲兄挥行?shù)據(jù)集和最小的負(fù)載。但是當(dāng)成千上萬(wàn)的用戶同時(shí)讀取數(shù)據(jù)庫(kù)中 上百萬(wàn)條記錄的時(shí)候,它就很可能會(huì)崩潰。
不幸的 是,這些問(wèn)題一開(kāi)始并不明顯,直到系 統(tǒng)增大、突然開(kāi)始崩潰的時(shí)候才會(huì)顯現(xiàn)出來(lái)。在增大的過(guò)程中,數(shù)據(jù)庫(kù)系統(tǒng)看起來(lái)運(yùn)行得很快(因?yàn)閿?shù)據(jù)都位于內(nèi)存中,而且很少有并發(fā)的查詢),并且對(duì)用戶的響 應(yīng)也很快,但實(shí)際上它的內(nèi)部運(yùn)行效率很低。這并不重要,我們關(guān)注的是在系統(tǒng)增大并遇到性能問(wèn)題之前找到這些問(wèn)題并加以解決。
關(guān)于這個(gè)問(wèn)題有很多不錯(cuò)的書(shū)和站點(diǎn)進(jìn)行了解 析,其中的關(guān)鍵工具包括慢查詢?nèi)罩?、INNODB狀態(tài)系統(tǒng),以及描述當(dāng)前性能的MySQL統(tǒng)計(jì)信息。我們見(jiàn)到過(guò)很多系統(tǒng)每秒會(huì)讀取500,000條數(shù)據(jù), 這是出現(xiàn)SQL問(wèn)題的明顯預(yù)兆,但公司往往對(duì)其一無(wú)所知直到服務(wù)器開(kāi)始崩潰。
MySQL系統(tǒng)應(yīng)該對(duì)所有數(shù)據(jù)使用 INNODB存儲(chǔ)引擎,因?yàn)镮NNODB與之前的MyISAM相比,運(yùn)行得更快、更穩(wěn)定,并且管理性能和備份工作也更加容易和快捷。在主配置文件 中,INNODB應(yīng)該被設(shè)置為默認(rèn)的數(shù)據(jù)庫(kù)引擎,并且系統(tǒng)應(yīng)該不時(shí)地進(jìn)行檢查,看是否意外創(chuàng)建了MyISAM的表。
總要擁有良好的DB配置和備份
很多公司都沒(méi)有良好的備份機(jī)制,也不知道如 何恰當(dāng)?shù)赝瓿蛇@項(xiàng)工作。MySQL的Dump是不夠的,因?yàn)樽詈玫膫浞莘椒ㄊ鞘褂肔VM快照和INNODB對(duì)系統(tǒng)進(jìn)行熱備份,從而得到超快的速度和超高的 可靠性。
另外,在將所有備份文件從服務(wù)器上轉(zhuǎn)移出來(lái) 之前要進(jìn)行壓縮和加密。另外還要確保擁有設(shè)計(jì)合理的MySQL配置。MySQL默認(rèn)安裝使用說(shuō)明中只有5~10行關(guān)于配置的說(shuō)明,這根本不適合開(kāi)發(fā)使用。 而我們提供給客戶的最佳實(shí)踐文檔足足有10頁(yè)那么長(zhǎng)。文檔中大約有100種有用的關(guān)于安全、性能和穩(wěn)定性問(wèn)題的設(shè)定,包括防止數(shù)據(jù)敗壞,其中很多設(shè)定都是 非常重要的。
使用讀/寫數(shù)據(jù)庫(kù)分離
隨著系統(tǒng)變得越來(lái)越龐大,特別是當(dāng)它們擁有 很差的SQL時(shí),一臺(tái)數(shù)據(jù)庫(kù)服務(wù)器通常不足以處理負(fù)載。但是多個(gè)數(shù)據(jù)庫(kù)意味著重復(fù),除非你對(duì)數(shù)據(jù)進(jìn)行了分離。更一般地,這意味著建立主/從副本系統(tǒng),其中 程序會(huì)對(duì)主庫(kù)編寫所有的Update、Insert和Delete變更語(yǔ)句,而所有Select的數(shù)據(jù)都讀取自從數(shù)據(jù)庫(kù)(或者多個(gè)從數(shù)據(jù)庫(kù))。
盡管概念上很簡(jiǎn)單,但是想要合理、精確地實(shí) 現(xiàn)并不容易,這可能需要大量的代碼工作。因此,即便在開(kāi)始時(shí)使用同一臺(tái)數(shù)據(jù)庫(kù)服務(wù)器,也要盡早計(jì)劃在PHP中使用分離的DB連接來(lái)進(jìn)行讀寫操作。如果正確 地完成該項(xiàng)工作,那么系統(tǒng)就可以擴(kuò)展到2臺(tái)、3臺(tái)甚至12臺(tái)服務(wù)器,并具備高可用性和穩(wěn)定性。
使用類似Memcached之類的數(shù)據(jù)庫(kù)緩存
即便有了好的數(shù)據(jù)庫(kù)設(shè)計(jì)、SQL和讀寫分 離,大型的系統(tǒng)仍然需要更快的性能,特別是對(duì)會(huì)話狀態(tài)、好友列表以及BBS文字之類的東西。為了達(dá)到這個(gè)目的,我們可以使用像MemCached之類的數(shù) 據(jù)緩存,它是一個(gè)高性能的簡(jiǎn)單數(shù)據(jù)緩存,已經(jīng)被所有最大型的站點(diǎn)使用。但是要小心的是,不要100%依賴于一臺(tái)Memcache服務(wù)器來(lái)提高性能,因?yàn)槿?果那臺(tái)服務(wù)器崩潰了,就會(huì)破壞整個(gè)系統(tǒng)的性能。在這種情況下,應(yīng)該使用2~3臺(tái)Memcache服務(wù)器形成簇集架構(gòu),并且有選擇地包含一個(gè)緩存準(zhǔn)備過(guò)程, 如果緩存服務(wù)器重啟,需要重新載入數(shù)據(jù),它能夠快速地載入緩存。
構(gòu)建測(cè)試和開(kāi)發(fā)環(huán)境
很多公司只有開(kāi)發(fā)者的桌面系統(tǒng)和他們的生產(chǎn) 服務(wù)器。當(dāng)系統(tǒng)變得越來(lái)越大、越來(lái)越復(fù)雜時(shí),測(cè)試和管理代碼就會(huì)導(dǎo)致嚴(yán)重的問(wèn)題。最佳的實(shí)踐是擁有兩個(gè)測(cè)試系統(tǒng),一個(gè)用于開(kāi)發(fā)者的代碼和功能的整合測(cè)試, 另一個(gè)要與生產(chǎn)環(huán)境完全一致,從而更容易向生產(chǎn)環(huán)境平滑地過(guò)渡。幸運(yùn)的是,現(xiàn)在使用云計(jì)算(或者私有云)可以輕松達(dá)到這一點(diǎn)。一個(gè)5~10臺(tái)服務(wù)器的生產(chǎn) 環(huán)境,可以很容易地在辦公室或者IDC中使用一臺(tái)服務(wù)器來(lái)復(fù)制,從而用于測(cè)試,而這臺(tái)服務(wù)器我們可以用于多個(gè)客戶的項(xiàng)目。
使 用版本控制
最后,要對(duì)一切使用版本控制,包括測(cè)試和生 產(chǎn)環(huán)境的部署。很多開(kāi)發(fā)者都使用SVN或者類似的方法。在理想狀態(tài)下,這些方法可以被用于所有代碼、腳本、HTML、圖片、配置、文檔和測(cè)試。版本控制應(yīng) 該是代碼轉(zhuǎn)移到測(cè)試環(huán)境的必經(jīng)之路,而不是簡(jiǎn)單地復(fù)制或者使用tar文件,因?yàn)檫@二者都是不可靠的。開(kāi)發(fā)者應(yīng)該將所有一切都簽入,打上標(biāo)簽,然后將它們簽 出到測(cè)試系統(tǒng)。如果所有都沒(méi)問(wèn)題,那么它們會(huì)將該版本簽出到生產(chǎn)環(huán)境。
總結(jié)
不管 是在開(kāi)發(fā)還是在運(yùn)營(yíng)過(guò)程中,創(chuàng)建可靠的 高性能Web系統(tǒng)都有很多應(yīng)該注意的事項(xiàng)。本文試圖從可操作性和可靠性的角度討論最重要的幾點(diǎn)。當(dāng)你構(gòu)建和管理站點(diǎn)的時(shí)候,請(qǐng)不要忘了這些重要的問(wèn)題。遵 循這些規(guī)則會(huì)有助于確保系統(tǒng)長(zhǎng)久、良好地運(yùn)行。
作者簡(jiǎn)介:
Steve Mushero,ChinaNetCloud 公司聯(lián)合創(chuàng)始人、CEO兼CTO,擁有全球20多年的技術(shù)管理經(jīng)驗(yàn)。曾擔(dān)任土豆網(wǎng)、Intermind和 Advanced Management Systems等多家企業(yè)CTO
譯者簡(jiǎn)介:
侯伯薇,生于鳳城,學(xué)在春城,做過(guò)國(guó)內(nèi)和對(duì) 日項(xiàng)目,現(xiàn)在大連某保險(xiǎn)公司工作。樂(lè)于鉆研技術(shù),同時(shí)注重業(yè)務(wù)知識(shí),勤于思考,愿意與人交流和分享。
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號(hào)聯(lián)系: 360901061
您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺(jué)我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長(zhǎng)非常感激您!手機(jī)微信長(zhǎng)按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對(duì)您有幫助就好】元
