配置文件是反模式

反模式哺哼,一般指一些已經(jīng)形成固定套路的錯(cuò)誤做法届巩,至于配置文件锁保,則是因?yàn)榭吹侥硞€(gè)微信群中有人抱怨redis的官方docker鏡像沒(méi)有暴露 redis.conf 岖瑰,我當(dāng)時(shí)不方便討論,只是說(shuō)了自己的觀點(diǎn)——“配置文件是反模式”豺妓。

實(shí)際上惜互,這個(gè)問(wèn)題我大約兩年前在文章中提到過(guò)——

...(后來(lái))大家形成共識(shí)——不用環(huán)境變量來(lái)約束和客制化軟件的行為(雖然環(huán)境變量本來(lái)就是做這個(gè)的)。這樣的后果是琳拭,c/c++训堆、java、python白嘁、ruby各種社區(qū)都形成了自己的配置文件習(xí)慣用法坑鱼,更進(jìn)一步,java社區(qū)曾經(jīng)有個(gè)趨勢(shì)絮缅,把配置文件本身都做的快和代碼沒(méi)區(qū)別了...

不知道有多少人注意這段話鲁沥,曾有同事反饋說(shuō)很有感觸呼股,我就略過(guò)了細(xì)節(jié)討論。然而兩年后我發(fā)現(xiàn)画恰,爭(zhēng)論不少卖怜,而其中的道理并沒(méi)有講透,本文就來(lái)補(bǔ)補(bǔ)課阐枣,專(zhuān)門(mén)討論一下配置文件的問(wèn)題。

什么才算配置奄抽?

不是配置的“配置”

web.xml蔼两、springXX.xml、ibatisXXX.xml......這些曾經(jīng)是Java工程師熟悉的“配置”逞度,在“約定大于配置”尚未流行的年代额划,這些東西非常繁瑣,常常為開(kāi)發(fā)人員詬病档泽,因?yàn)樗鼈冏龅氖虑槠鋵?shí)和代碼差不多俊戳,不用代碼寫(xiě)其實(shí)是因?yàn)椴环奖阌镁幊陶Z(yǔ)言簡(jiǎn)潔的表達(dá)出來(lái)。

Ruby on Rails也有類(lèi)似的文件馆匿,比如config目錄下的一些文件(包括routes.rb抑胎、application.rb,以及 initializers 目錄下面的文件等等)渐北,它們雖然被稱(chēng)為“配置”文件阿逃,但本身也只是ruby代碼。從某個(gè)角度看赃蛛,這些文件是為了對(duì)一些功能庫(kù)進(jìn)行針對(duì)性的配置恃锉,但是這種“配置”是一次性的、不變的呕臂,因而也可以看成是業(yè)務(wù)功能的編程實(shí)現(xiàn)破托。

上述這些情況有一個(gè)共同點(diǎn),這些所謂的“配置”文件歧蒋,內(nèi)容是在開(kāi)發(fā)階段就寫(xiě)好的土砂,在軟件交付以后,無(wú)論是測(cè)試還是線上疏尿,它們都不會(huì)被改變瘟芝,從軟件開(kāi)發(fā)的生命周期來(lái)看,它們和代碼一致褥琐,所以這個(gè)“配置”文件其實(shí)不是配置锌俱。

真正的配置

真正的配置是那些以 dev\test\pe\real 為前綴的內(nèi)容,比如數(shù)據(jù)庫(kù)設(shè)置——顯然敌呈,我們?cè)陂_(kāi)發(fā)階段和生產(chǎn)環(huán)境當(dāng)然不可能使用同一個(gè)數(shù)據(jù)庫(kù)贸宏。

# config/database.yml
development:
  ......
  database: shelter_development

test:
  ......
  database: shelter_test

production:
  <<: *default
  host: db
  database: shelter_production

這些配置體現(xiàn)的是應(yīng)用系統(tǒng)與外部環(huán)境之間的關(guān)系造寝,每一個(gè)配置項(xiàng)的值,都和具體環(huán)境有關(guān)吭练,其生命周期和代碼不同诫龙,而和軟件的部署運(yùn)行息息相關(guān)。

配置管理

由于配置項(xiàng)和運(yùn)行時(shí)相關(guān)鲫咽,當(dāng)軟件系統(tǒng)運(yùn)行起來(lái)的時(shí)候签赃,我們必須做的一件事就是為這些配置項(xiàng)提供真實(shí)的參數(shù)值,比如這樣:

production:
  <<: *default
  host: 192.168.16.103

我們知道分尸,這樣的內(nèi)容不應(yīng)該納入源碼控制锦聊,原因有三個(gè):

  1. 線上的實(shí)際情況需要保護(hù),直接納入源碼會(huì)導(dǎo)致安全風(fēng)險(xiǎn)
  2. 從線上運(yùn)維來(lái)看箩绍,一旦需要進(jìn)行調(diào)整(比如更換數(shù)據(jù)庫(kù))孔庭,應(yīng)該用比較簡(jiǎn)單的方式快速切換,“commit-push-deploy”流程對(duì)這種調(diào)整是過(guò)于繁瑣的
  3. 對(duì)源代碼進(jìn)行的版本控制和這樣的內(nèi)容難以協(xié)調(diào)材蛛,比如線上有多個(gè)應(yīng)用實(shí)例圆到,配置項(xiàng)的值不同等等

這里的根本原因還是在于配置項(xiàng)和代碼的生命周期不同,它們本來(lái)就是兩回事卑吭。

通常的運(yùn)維工作中芽淡,管理配置項(xiàng)和源碼的版本控制系統(tǒng)是獨(dú)立的,我見(jiàn)過(guò)一種做法陨簇,線上維護(hù)一套git吐绵,把所有的線上配置文件都放進(jìn)去,然后用一個(gè)自動(dòng)化腳本去執(zhí)行分發(fā)河绽。

如果運(yùn)用docker技術(shù)己单,我們大概會(huì)看到這種做法:

docker run ... -v /home/admin/conf/db.yml:/usr/src/app/config/database.yml ...

出于標(biāo)準(zhǔn)化和自動(dòng)化的需要,這行代碼大約會(huì)在某個(gè)capistrano腳本或者jenkins任務(wù)中固化下來(lái)耙饰,實(shí)際上這也是本文最開(kāi)始那個(gè)問(wèn)題所得到的建議答案纹笼。

這個(gè)做法是可行的,遺憾的是這并不漂亮苟跪,我們?cè)谂渲庙?xiàng)中需要反映的是環(huán)境的特殊性(在這里廷痘,指的是數(shù)據(jù)庫(kù)服務(wù)器的host),而對(duì)每個(gè)這樣的配置項(xiàng)件已,運(yùn)維的工具需要管理的信息至少包括4個(gè):

  • 配置項(xiàng)名稱(chēng): host
  • 配置項(xiàng)的值: 192.168.16.103
  • 配置文件的位置: /home/admin/conf/db.yml
  • 配置文件的目標(biāo)掛載點(diǎn): /usr/src/app/config/database.yml

顯然笋额,前兩個(gè)才是我們要關(guān)心的信息,后兩條并不重要篷扩。

因?yàn)檫@里出現(xiàn)了“間接性”兄猩,由于我們把配置項(xiàng)的名-值綁定放在了文件中,因此運(yùn)維工具并不能直接管理這個(gè)綁定關(guān)系,而是迂回的去管理那個(gè)文件枢冤,并由此引入了掛載點(diǎn)的概念鸠姨。

這似乎是個(gè)小問(wèn)題,其實(shí)不然淹真,這種間接管理除了反直覺(jué)以外讶迁,至少還有兩個(gè)后果:

  1. 配置項(xiàng)統(tǒng)一管理的困難,無(wú)論你使用什么運(yùn)維工具核蘸,工具并不知道自己管理了哪些配置項(xiàng)巍糯,這使得我們?nèi)狈σ粋€(gè)全局視圖,未來(lái)要進(jìn)行配置項(xiàng)的優(yōu)化就會(huì)比較麻煩——這個(gè)后果是客扎,很多團(tuán)隊(duì)要再額外開(kāi)發(fā)一個(gè)“配置管理系統(tǒng)”鳞贷,然后讓這個(gè)管理系統(tǒng)負(fù)責(zé)維護(hù)掛載點(diǎn)和文件實(shí)際位置的綁定關(guān)系。
  2. 配置項(xiàng)管理不能標(biāo)準(zhǔn)化虐唠。顯然,一個(gè) java 應(yīng)用中的 db.properties 和 Rails 應(yīng)用中的 database.yml 的文件在本質(zhì)上具有相同的數(shù)據(jù)結(jié)構(gòu)惰聂,但使用起來(lái)卻大相徑庭疆偿,于是,我們上面所說(shuō)的那個(gè)“配置管理系統(tǒng)”需要理解五花八門(mén)的配置文件格式搓幌,而且不但要能讀取杆故,還要能——不丟失信息的進(jìn)行寫(xiě)入(于是還要處理配置文件中的comment內(nèi)容等很多問(wèn)題)

所以使用配置文件并不是一個(gè)好主意,更好的做法是使用環(huán)境變量——

production:
  <<: *default
  host: <%= ENV['db_host'] %>

此時(shí)溉愁,在docker中啟用容器就會(huì)是這樣:

docker run ... -e db_host='192.168.16.103' ...

乍一看处铛,這么做顯得有些落后,特別是配置項(xiàng)較多的時(shí)候顯得比較麻煩拐揭,不過(guò)實(shí)踐中我們并不會(huì)直接這么做撤蟆,借助運(yùn)維工具,這些配置項(xiàng)可以系統(tǒng)的進(jìn)行管理——去除了文件這個(gè)中間環(huán)節(jié)以后堂污,配置管理這件事就會(huì)變得很簡(jiǎn)單家肯,一個(gè)標(biāo)準(zhǔn)的開(kāi)源軟件就可以承擔(dān)了。

當(dāng)然盟猖,這里應(yīng)該管理那些真正的配置項(xiàng)讨衣,那些不是配置的“配置”無(wú)需這么做。

配置的本質(zhì)

接下來(lái)式镐,讓我們深入一步反镇,討論一下配置這件事的本質(zhì)是什么。所謂軟件娘汞,本質(zhì)上其實(shí)就是一個(gè)程序而已歹茶,一個(gè)簡(jiǎn)單的程序代碼可以是這樣運(yùn)行的:

def area
  5 ** 2 * Math::PI
end

我們用它來(lái)計(jì)算一個(gè)半徑為5的圓面積,執(zhí)行一下

irb(main):001:0> area
=> 78.53981633974483

不過(guò),這段代碼只能計(jì)算固定半徑辆亏,我們一般會(huì)改成這樣:

def area r
  r ** 2 * Math::PI
end

執(zhí)行一下

irb(main):001:0> area 5
=> 78.53981633974483
irb(main):002:0> area 7
=> 153.93804002589985

修改前后的差別是添加了函數(shù)的參數(shù)风秤,程序設(shè)計(jì)課老師會(huì)告訴我們,函數(shù)里的 r 是形式參數(shù)扮叨,調(diào)用時(shí)我們用數(shù)字 5 或者 7 來(lái)代替這個(gè) r 缤弦,調(diào)用時(shí)的參數(shù)值也叫做實(shí)際參數(shù)。

這個(gè)例子很簡(jiǎn)單彻磁,然而蘊(yùn)含著一個(gè)很重要的思路——參數(shù)化碍沐,核心是把我們能確定的東西固化為某種產(chǎn)品單元(函數(shù)、類(lèi)衷蜓、程序等等)累提,然后通過(guò)參數(shù)讓其具備適應(yīng)能力,能夠靈活適應(yīng)多種不同的運(yùn)行上下文磁浇。

所以斋陪,參數(shù)賦值過(guò)程其實(shí)就是產(chǎn)品單元得到運(yùn)行上下文的過(guò)程,我們也可以看做是對(duì)環(huán)境的綁定置吓,按照這個(gè)思路无虚,函數(shù)參數(shù)化就是帶參數(shù)的函數(shù),類(lèi)的參數(shù)化就是攜帶實(shí)例變量的對(duì)象實(shí)例衍锚,而應(yīng)用程序的參數(shù)化就是我們上面所說(shuō)的配置項(xiàng)友题,正是在這個(gè)意義上,我們才能更好的理解UNIX的先驅(qū)們?yōu)槭裁匆阋粋€(gè)環(huán)境變量的機(jī)制戴质。

強(qiáng)調(diào)一遍:所謂配置項(xiàng)就是對(duì)應(yīng)用軟件進(jìn)行參數(shù)化度宦,環(huán)境變量設(shè)定就是實(shí)參對(duì)形參的綁定

程序和進(jìn)程的關(guān)系非常像類(lèi)和實(shí)例的關(guān)系告匠,每當(dāng)一個(gè)程序被“實(shí)例化”——也就是啟動(dòng)運(yùn)行一個(gè)進(jìn)程時(shí)戈抄,操作系統(tǒng)會(huì)對(duì)環(huán)境變量進(jìn)行綁定,這樣后专,這個(gè)進(jìn)程就會(huì)按照啟動(dòng)時(shí)所做的設(shè)定調(diào)整自己的運(yùn)行邏輯呛凶,以適應(yīng)外在環(huán)境。

可能有人會(huì)注意到行贪,環(huán)境變量的綁定是進(jìn)程啟動(dòng)時(shí)進(jìn)行的漾稀,而運(yùn)行過(guò)程中一般不能改變它的值,這是不是設(shè)計(jì)上的不足呢建瘫?

并不是崭捍,可以設(shè)想一下相反的設(shè)計(jì),如果環(huán)境變量的綁定是隨時(shí)進(jìn)行的啰脚,那么我們將面對(duì)一個(gè)行為方式不確定的軟件系統(tǒng)殷蛇,一旦進(jìn)程運(yùn)行出現(xiàn)問(wèn)題实夹,我們要分析的現(xiàn)場(chǎng),會(huì)包括很多不確定其實(shí)際值的參數(shù)粒梦,這對(duì)問(wèn)題的分析常常是個(gè)災(zāi)難亮航。

配置看似是一個(gè)小問(wèn)題,然而當(dāng)我們認(rèn)真考察以后匀们,對(duì)系統(tǒng)的架構(gòu)缴淋、組件之間、組件與外部環(huán)境之間的關(guān)系會(huì)有更深入的理解泄朴,我一直很喜歡《三體》里章北海父親臨終前的建議“多想想”重抖,是的,遇到問(wèn)題多想想祖灰,想清楚再動(dòng)手钟沛,會(huì)事半功倍。

思考題:配置問(wèn)題與“開(kāi)閉原則(Open/Closed Principle)”有什么關(guān)系局扶?

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末恨统,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子三妈,更是在濱河造成了極大的恐慌延欠,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,544評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件沈跨,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡兔综,警方通過(guò)查閱死者的電腦和手機(jī)饿凛,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,430評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)软驰,“玉大人涧窒,你說(shuō)我怎么就攤上這事《Э鳎” “怎么了纠吴?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,764評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)慧瘤。 經(jīng)常有香客問(wèn)我戴已,道長(zhǎng),這世上最難降的妖魔是什么锅减? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,193評(píng)論 1 292
  • 正文 為了忘掉前任糖儡,我火速辦了婚禮,結(jié)果婚禮上怔匣,老公的妹妹穿的比我還像新娘握联。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,216評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布金闽。 她就那樣靜靜地躺著纯露,像睡著了一般。 火紅的嫁衣襯著肌膚如雪代芜。 梳的紋絲不亂的頭發(fā)上埠褪,一...
    開(kāi)封第一講書(shū)人閱讀 51,182評(píng)論 1 299
  • 那天,我揣著相機(jī)與錄音蜒犯,去河邊找鬼组橄。 笑死,一個(gè)胖子當(dāng)著我的面吹牛罚随,可吹牛的內(nèi)容都是我干的玉工。 我是一名探鬼主播,決...
    沈念sama閱讀 40,063評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼淘菩,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼遵班!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起潮改,我...
    開(kāi)封第一講書(shū)人閱讀 38,917評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤狭郑,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后汇在,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體翰萨,經(jīng)...
    沈念sama閱讀 45,329評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,543評(píng)論 2 332
  • 正文 我和宋清朗相戀三年糕殉,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了亩鬼。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,722評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡阿蝶,死狀恐怖雳锋,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情羡洁,我是刑警寧澤玷过,帶...
    沈念sama閱讀 35,425評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站筑煮,受9級(jí)特大地震影響辛蚊,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜真仲,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,019評(píng)論 3 326
  • 文/蒙蒙 一嚼隘、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧袒餐,春花似錦飞蛹、人聲如沸谤狡。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,671評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)墓懂。三九已至,卻和暖如春霉囚,著一層夾襖步出監(jiān)牢的瞬間捕仔,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,825評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工盈罐, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留榜跌,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,729評(píng)論 2 368
  • 正文 我出身青樓盅粪,卻偏偏與公主長(zhǎng)得像钓葫,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子票顾,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,614評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理础浮,服務(wù)發(fā)現(xiàn),斷路器奠骄,智...
    卡卡羅2017閱讀 134,652評(píng)論 18 139
  • Ubuntu的發(fā)音 Ubuntu,源于非洲祖魯人和科薩人的語(yǔ)言蝉绷,發(fā)作 oo-boon-too 的音鸭廷。了解發(fā)音是有意...
    螢火蟲(chóng)de夢(mèng)閱讀 99,257評(píng)論 9 467
  • Spring Boot 參考指南 介紹 轉(zhuǎn)載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 46,804評(píng)論 6 342
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語(yǔ)法,類(lèi)相關(guān)的語(yǔ)法潜必,內(nèi)部類(lèi)的語(yǔ)法,繼承相關(guān)的語(yǔ)法沃但,異常的語(yǔ)法磁滚,線程的語(yǔ)...
    子非魚(yú)_t_閱讀 31,623評(píng)論 18 399
  • 我和我的祖國(guó) 9月,隨著省委組織的“擁戴核心合作共進(jìn)”主題教育培訓(xùn)班來(lái)到寧夏宵晚,傾聽(tīng)專(zhuān)家講解垂攘,追尋紅...
    小夕RY閱讀 631評(píng)論 0 0