注意看這兩個(gè)目錄腻惠,執(zhí)行Python腳本环肘,版本卻不一樣
這是同一個(gè)服務(wù)器跑的ruby on rails
這個(gè)是在同一個(gè)服務(wù)器跑nodejs
那么這些程序都是跑在一個(gè)服務(wù)器上面,我們?cè)趺醋尪鄠€(gè)程序搭建在一個(gè)服務(wù)器上集灌,搭建一個(gè)適合于架構(gòu)師使用的開發(fā)與實(shí)現(xiàn)環(huán)境(Linux)悔雹。
眾所周知,當(dāng)項(xiàng)目越來越大的時(shí)候欣喧,項(xiàng)目的設(shè)計(jì)也會(huì)越來越復(fù)雜腌零,一個(gè)線上運(yùn)營的商業(yè)項(xiàng)目往往會(huì)涉及到多種編程語言與技術(shù)的使用,比如php+nodejs唆阿,ruby+nodejs益涧,java、python等驯鳖。
如果你覺得為這些不同種類的技術(shù)搭建環(huán)境就已經(jīng)夠頭痛了闲询,那么我想告訴你,還有更復(fù)雜的問題還在后頭浅辙,不同語言環(huán)境也是在不斷發(fā)展之中的扭弧。
php從4.9到5.2再到7.0中間有著巨大的變化,代碼存在著大量的不兼容记舆。
java 1.x到j(luò)ava 6鸽捻、7 再到j(luò)ava8不止是語法上的變化,甚至連程序設(shè)計(jì)的思想與原則都發(fā)生了改變。
類似的例子還有python和ruby御蒲,這兩個(gè)語言在linux運(yùn)維中使用非常廣泛衣赶,很多自動(dòng)化運(yùn)維腳本都是用python2.x與ruby1.x編寫的,然而現(xiàn)在社區(qū)中活躍的新版本確是python3.x和ruby2.x删咱。
大家不要覺得這些個(gè)語言與你們無關(guān)屑埋,很有可能你們?cè)趌inux中使用的某些工具就是用老版本的python和ruby編寫的。
這么多的問題痰滋,是不是已經(jīng)讓大家頭大了呢摘能?
再大規(guī)模商業(yè)項(xiàng)目中,解決環(huán)境敲街、版本等問題其實(shí)有很成熟的解決方案团搞。
現(xiàn)在的很多云計(jì)算廠商,利用虛擬化技術(shù)多艇、容器技術(shù)逻恐,采用服務(wù)化方式進(jìn)行開發(fā),比如現(xiàn)在流行的docker+微服務(wù)架構(gòu)就是其中的佼佼者峻黍。
要完成這種規(guī)模的商業(yè)項(xiàng)目架構(gòu)設(shè)計(jì)复隆,架構(gòu)師常需要進(jìn)行程序測(cè)試與驗(yàn)證設(shè)計(jì),可要在筆記本上完成這樣的部署姆涩,無論是利用虛擬化技術(shù)(虛擬機(jī))還是容器技術(shù)挽拂,都顯得太重了,架構(gòu)師需要的應(yīng)該是一個(gè)全能的開發(fā)骨饿、部署與運(yùn)維環(huán)境亏栈,環(huán)境需要與產(chǎn)品環(huán)境高度一致,而且還應(yīng)該輕便宏赘,節(jié)省性能绒北,方便管理。
那我們今天就來看一看察署,這樣的環(huán)境是如何搭建起來的闷游。
這邊我已經(jīng)提前準(zhǔn)備好了一臺(tái)主機(jī)來為大家實(shí)驗(yàn)。
先從最簡(jiǎn)單的部分開始箕母。
我們?nèi)绾巫屢慌_(tái)服務(wù)器同時(shí)支持多種不同的web服務(wù)器储藐,比如如何同時(shí)讓我們的這臺(tái)測(cè)試機(jī)上面能跑起php,nodejs嘶是,ruby on rails,如果有必要的話再加一個(gè)tomcat來跑jsp也是可以的蛛碌。
通常情況下聂喇,如果在一臺(tái)機(jī)器中啟動(dòng)多個(gè)http服務(wù),我們必須給這些web服務(wù)分配不同的端口,否則就會(huì)端口沖突希太。然而標(biāo)準(zhǔn)的網(wǎng)頁服務(wù)走的是80端口克饶,在瀏覽器中輸入一個(gè)網(wǎng)址,不指定端口號(hào)誊辉,者這個(gè)請(qǐng)求就會(huì)發(fā)送到80端口矾湃。
這樣說來,如果同時(shí)在多個(gè)端口開啟web服務(wù)基本不需要做太多額外的事情堕澄,只要去各個(gè)服務(wù)器軟件中修改配置文件邀跃,把端口號(hào)調(diào)整為其它大于一千的端口且不重復(fù),這樣web服務(wù)就能跑起來了蛙紫。
接著我們需要解決一個(gè)棘手的問題拍屑,通常我們?cè)O(shè)計(jì)的restfulAPi接口以及默認(rèn)的用戶訪問,都是直接把http請(qǐng)求發(fā)送至80端口的坑傅,80端口只有一個(gè)僵驰,自然是只能有一個(gè)程序去監(jiān)聽這個(gè)端口,不過這個(gè)解決起來也很簡(jiǎn)單唁毒。
之所以說這個(gè)最簡(jiǎn)單蒜茴,是因?yàn)楹芏鄅ttp服務(wù)器程序都支持http代理功能。我們?cè)谶@里即將介紹的是利用nginx反向代理實(shí)現(xiàn)多個(gè)web服務(wù)共享80端口浆西。
第一步粉私,先讓nginx在80端口啟動(dòng),nginx啟動(dòng)成功后再讓其它服務(wù)器在別的端口啟動(dòng)服務(wù)室谚。
為了保險(xiǎn)期間毡鉴,自己可以先測(cè)試一下,各個(gè)服務(wù)在不同端口是否工作正常秒赤。
然后我們就需要為各個(gè)服務(wù)創(chuàng)建虛擬主機(jī)猪瞬,由于多個(gè)服務(wù)跑在一臺(tái)機(jī)器上,所以為了能有所區(qū)分入篮,我們得給這些服務(wù)分別綁定不同域名陈瘦,如果是在本機(jī)測(cè)試,域名綁定的工作可以選擇在自己筆記本的宿主機(jī)操作系統(tǒng)里面修改hosts文件潮售。
檢查一下如果在瀏覽器中輸入我們綁定的域名可以訪問到服務(wù)器痊项,證明綁定成功。
這一步完成以后酥诽,我們?cè)趎ginx的配置中增加虛擬主機(jī)配置鞍泉,為了方便管理,每一個(gè)虛擬主機(jī)的配置最好是能有一個(gè)獨(dú)立的文件存放肮帐。
下面是一個(gè)點(diǎn)心的虛擬主機(jī)反向代理配置
server {
listen 80;
server_name test.ydma.cn;
location / {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-Nginx-Proxy true;
proxy_pass http://127.0.0.1:9001;
}
}
如果是在ubuntu中咖驮,這個(gè)文件應(yīng)該放在/etc/nginx/sites-available里面边器,然后再通過ln -s 做一個(gè)軟連接放在sites-enabled文件夾中。
之所以nginx能找到這個(gè)配置文件是因?yàn)樵趎ginx.conf主配置文件中有這么兩行配置托修。
include /etc/nginx/conf.d/.conf;
include /etc/nginx/sites-enabled/;
我們可以看到上面的配置文件中 由nginx監(jiān)聽主機(jī)名為 server_name test.ydma.cn 忘巧,
端口號(hào) listen 80;
location設(shè)置為 proxy_pass http://127.0.0.1:9001;
也就是說把訪問test.ydma.cn:80的請(qǐng)求交給127.0.0.1的9001端口去處理。
proxy_set_header的配置是為了將http請(qǐng)求的頭部信息做相應(yīng)的改寫睦刃,如果沒有這些設(shè)置砚嘴,轉(zhuǎn)發(fā)后的請(qǐng)求得到的請(qǐng)求信息就是nginx服務(wù)的信息,而不是外部用戶的信息涩拙。
把這個(gè)文件復(fù)制幾分际长,只需要改寫server_name以及proxy_pass后的端口號(hào),那么我們就可以讓nodes 吃环、ruby on rails 也颤、apache、等多個(gè)web服務(wù)同時(shí)共用80端口跑起來了郁轻。
多個(gè)web服務(wù)跑起來了翅娶,共用80端口了,這樣服務(wù)與服務(wù)之間就可以通過標(biāo)準(zhǔn)的http協(xié)議做webservice調(diào)用了好唯。
可現(xiàn)在還不是高興的時(shí)候竭沫,因?yàn)槁闊┑倪€在后頭,在項(xiàng)目開發(fā)過程中骑篙,我們用到的各種工具和軟件蜕提,通常會(huì)有依賴,比如hadoop靶端,我們以hadoop2.x為例谎势,它依賴jdk7,我們制作的一款機(jī)器學(xué)習(xí)軟件大量使用java函數(shù)式編程杨名,需要jdk8脏榆,那么我們?cè)趺礃咏鉀Q這個(gè)問題呢?
通常做法是同時(shí)安裝兩個(gè)版本到不同路徑台谍,比如/opt/java/jdk/1.7.x/目錄下安裝java7须喂,/opt/java/jdk/1.8.x/下安裝java8,然后可以在linux中配置用戶級(jí)別環(huán)境變量JAVA_HOME趁蕊,通常環(huán)境變量應(yīng)該寫在文件 ~./bashrc 或者~/.bash_profile中,然后再修改系統(tǒng)的PATH變量坞生,指向JAVA_HOME下的bin目錄,這樣我希望用戶默認(rèn)環(huán)境下是java8那么我就把環(huán)境變量JAVA_HOME切換到j(luò)ava8的安裝目錄就可以了掷伙,這樣我們自己寫的機(jī)器學(xué)習(xí)工具就能在我的用戶權(quán)限下跑起來了是己。
如果我的機(jī)器學(xué)習(xí)程序需要訪問hadoop中的數(shù)據(jù)怎么辦?hadoop2.x需要的是jdk7任柜,而且要和我的java8程序一起在同一臺(tái)機(jī)器中使用赃泡。
linux中的很多軟件都會(huì)有啟動(dòng)腳本寒波,如果在啟動(dòng)腳本中設(shè)定完整的java運(yùn)行路徑乘盼,就可以用指定的java版本來運(yùn)行程序了升熊,像hadoop這樣的大型開源項(xiàng)目,已經(jīng)一早就為我們準(zhǔn)備了運(yùn)行時(shí)環(huán)境設(shè)置的途徑绸栅。
是不是依葫蘆畫瓢级野,然后其它語言多版本的問題都可以這樣結(jié)局呢?
事情遠(yuǎn)遠(yuǎn)比我們想象中復(fù)雜粹胯,比如python蓖柔,我們?yōu)榱耸褂矛F(xiàn)在社區(qū)中一些新的python工具,必須把python升級(jí)到3.x版本风纠,但操作系統(tǒng)中有些操作也需要python况鸣,比如當(dāng)前ubuntu中用的是2.7,如果我們升級(jí)了python竹观,系統(tǒng)的有些功能會(huì)無法正常使用镐捧。
ruby情況與python類似,由于ruby項(xiàng)目高度活躍臭增,我們很可能需要同時(shí)使用3個(gè)以上甚至更多的ruby版本在一個(gè)系統(tǒng)中懂酱,不止如此,還需要能靈活的管理我們的環(huán)境和版本誊抛。
有一個(gè)很好的設(shè)計(jì)思路能解決這個(gè)問題列牺,無論你希望有多少個(gè)版本并存在操作系統(tǒng)且無沖突。
原理如下:
首先拗窃,我們需要能把語言各個(gè)版本環(huán)境統(tǒng)一管理瞎领,也就是說把所有我們需要的版本都下載下來,集中存放随夸。
然后我們建立一個(gè)該語言環(huán)境的目錄加入PATH九默,為語言環(huán)境以及bing中的命令建立替身,所有敲的命令以及執(zhí)行的腳本默認(rèn)都指向替身而不是命令本身逃魄,比如正常情況下如果敲python命令荤西,系統(tǒng)會(huì)去path中查找python文件,然后執(zhí)行伍俘,由于通過path找到的python是替身而不是命令本身邪锌,我們可以利用替身幫我們做一些事情。
我們可以指明某個(gè)文件夾必須使用某個(gè)版本的python癌瘾,這樣替身接到命令調(diào)用時(shí)就會(huì)先判斷觅丰,這個(gè)文件夾是否有指明版本,如果有指定妨退,就再去調(diào)用指定版本的python去執(zhí)行命令妇萄,當(dāng)然如果沒有指定文件夾蜕企,那么python就判斷,當(dāng)前登陸的用戶有沒有指定python版本冠句,如果沒有指定就再判斷轻掩,有沒有指定默認(rèn)全局python版本,這樣一來懦底,我可以精確的去控制每一個(gè)文件夾唇牧,每一個(gè)用戶,以及整個(gè)操作系統(tǒng)應(yīng)該是用的python版本聚唐。
同樣的原理也適用于其它編程語言丐重,而且也有這樣的開源項(xiàng)目來幫助我們做這樣的事情。
最早這樣管理環(huán)境的是ruby杆查,ruby工程師發(fā)明了很多項(xiàng)目管理工具扮惦,早期的rvm就是管理版本環(huán)境與依賴的,功能比較強(qiáng)大亲桦,而另一個(gè)輕型解決方案叫做rbenv就是用我上面講的辦法來實(shí)現(xiàn)復(fù)雜的環(huán)境的管理崖蜜。
由于有了rbenv這樣好用的工具管理ruby,因此就有人利用rbenv進(jìn)行改造烙肺,誕生了pyenv來管理python纳猪,還有phpenv等等。