使用confd + etcd實現(xiàn)Nginx動態(tài)配置

etcd

etcd是一個高可用的分布式鍵值(key-value)數(shù)據(jù)庫枪狂。內(nèi)部采用raft協(xié)議作為一致性算法危喉,基于Go語言實現(xiàn)。etcd數(shù)據(jù)庫與redis類似州疾,其獨特性在于:

  1. 分布式部署辜限,擴展性強,且數(shù)據(jù)和事務(wù)保持一致
  2. 提供watch接口严蓖,可監(jiān)聽多個鍵的變化
  3. 對于單個鍵而言薄嫡,每次更新其值都會保留上一個版本,可以對鍵進行版本回溯
  4. ttl使用租約實現(xiàn)

etcd更強調(diào)的是各個節(jié)點之間的通信颗胡,同步毫深,確保各個節(jié)點上數(shù)據(jù)和事務(wù)的一致性,使得服務(wù)更穩(wěn)定毒姨,本身單節(jié)點的寫入能力并不強哑蔫。
redis更像是內(nèi)存型緩存,雖然也有cluster做主從同步和讀寫分離,但節(jié)點間的一致性主要強調(diào)的是數(shù)據(jù)闸迷,并不在乎事務(wù)嵌纲,因此讀寫能力很強,qps甚至可以達到10萬+

安裝

$ mkdir etcd
$ cd etcd
$ curl -L https://github.com/etcd-io/etcd/releases/download/v3.3.13/etcd-v3.3.13-linux-amd64.tar.gz -o 
$ ./etcd-v3.3.13-linux-amd64.tar.gz
$ tar xzvf etcd-v3.3.13-linux-amd64.tar.gz --strip-components=1
$ ./etcd -version
$ ./etcdctl -version

本地多成員集群

針對單機用戶腥沽,開啟多進程逮走,模擬多機器集群(本次模擬開啟三個etcd集群)

1. 安裝go
2. 安裝goreman(進程管理工具)
$ go get github.com/mattn/goreman
3. 查看gopath
$ go env
GOPATH="/home/apple/go"

所以goreman運行路徑為home/apple/go/bin/goreman

4. 編寫goroman配置文件

goroman配置文件默認名稱為Procfile,可以更換,但啟動時今阳,需要通過-c指定配置文件
goroman管理進程的配置文件由 進程名:執(zhí)行命令組成

$ vim Procfile

編輯內(nèi)容如下

etcd1: ./etcd --name infra1 --listen-client-urls http://127.0.0.1:2379 --advertise-client-urls http://127.0.0.1:2379 --listen-peer-urls http://127.0.0.1:12380 --initial-advertise-peer-urls http://127.0.0.1:12    380 --initial-cluster-token etcd-cluster-1 --initial-cluster 'infra1=http://127.0.0.1:12380,infra2=http://127.0.0.1:22380,infra3=http://127.0.0.1:32380' --initial-cluster-state new --enable-pprof
etcd2: ./etcd --name infra2 --listen-client-urls http://127.0.0.1:22379 --advertise-client-urls http://127.0.0.1:22379 --listen-peer-urls http://127.0.0.1:22380 --initial-advertise-peer-urls http://127.0.0.1:    22380 --initial-cluster-token etcd-cluster-1 --initial-cluster 'infra1=http://127.0.0.1:12380,infra2=http://127.0.0.1:22380,infra3=http://127.0.0.1:32380' --initial-cluster-state new --enable-pprof
etcd3: ./etcd --name infra3 --listen-client-urls http://127.0.0.1:32379 --advertise-client-urls http://127.0.0.1:32379 --listen-peer-urls http://127.0.0.1:32380 --initial-advertise-peer-urls http://127.0.0.1:    32380 --initial-cluster-token etcd-cluster-1 --initial-cluster 'infra1=http://127.0.0.1:12380,infra2=http://127.0.0.1:22380,infra3=http://127.0.0.1:32380' --initial-cluster-state new --enable-pprof
5. 運行
$ /home/apple/go/bin/goreman -f  Procfile start

此時etcd集群被開啟

6.查看集群列表

此時代表集群正確安裝并啟動

$ export ETCDCTL_API=3
$ ./etcdctl member list
8211f1d0f64f3269: name=infra1 peerURLs=http://127.0.0.1:12380 clientURLs=http://127.0.0.1:2379 isLeader=false
91bc3c398fb3c146: name=infra2 peerURLs=http://127.0.0.1:22380 clientURLs=http://127.0.0.1:22379 isLeader=true
fd422379fda50e48: name=infra3 peerURLs=http://127.0.0.1:32380 clientURLs=http://127.0.0.1:32379 isLeader=false

etcd基本使用

API地址https://godoc.org/github.com/coreos/etcd/client
官方包提供了對于etcd所有操作的API

創(chuàng)建用于操作etcd鍵值的KeysAPI對象kv
var cli client.Client
var kv client.KeysAPI

func handleError(e error, msg string) {
    if e != nil {
        log.Fatal(e)
    }
    if msg != "" {
        log.Println(msg)
    }
}

func init(){
    cfg := client.Config{
        Endpoints: []string{"http://127.0.0.1:2379", "http://127.0.0.1:22379", "http://127.0.0.1:32379"},
    }
    var e error

    cli, e = client.New(cfg)
    handleError(e, "")
    kv = client.NewKeysAPI(cli)
}
設(shè)置鍵
func setVal(kv client.KeysAPI, key string, val string) {
    log.Printf("設(shè)置鍵%s值%s\n", key, val)
    _, e := kv.Set(context.Background(), key, val, nil)
    if e != nil {
        log.Println(e)
    }
}
獲取鍵值
func getVal(kv client.KeysAPI, key string) string {
    // 獲取鍵
    log.Printf("開始獲取鍵%s \n", key)
    resp, e := kv.Get(context.TODO(), key, nil)
    handleError(e, "")
    index, value := resp.Index, resp.Node.Value
    log.Printf("獲取當前版本:%d 值:%s", index, resp.Node.Value)
    return string(value)
}
創(chuàng)建文件夾

etcd的鍵值對存儲可以理解為文件存儲在目錄中言沐,鍵為目錄,值為文件
創(chuàng)建文件夾的目的為:使用etcd提供的watch方法可以監(jiān)控整個文件夾中鍵(即文件)的變化

func mkdir(kv client.KeysAPI, dir string) {
    o := client.SetOptions{Dir: true}
    _, e := kv.Set(context.Background(), dir, "", &o)
    handleError(e, "創(chuàng)建完成")
}
調(diào)用
func TestSetVal(t *testing.T) {
    setVal(kv, "/nginx/foo", "bar")
}

func TestGetVal(t *testing.T) {
    getVal(kv, "/nginx/foo")
}

func TestMkdir(t *testing.T) {
    mkdir(kv, "/nginx")
}

confd

輕量級的配置管理工具酣栈,主要有兩個目的

  1. 讀取etcd保存的配置信息险胰,同步到本地配置文件中,并保證本地配置文件是最新的
  2. 同步配置文件之后可以指定命令使配置生效

安裝

https://github.com/kelseyhightower/confd/blob/master/docs/installation.md

使用

把核心信息矿筝,比如upstream起便、server_name等存儲在etcd中,使用confd來自動生成nginx配置文件,并reload使配置生效

upstream www_test {
    server 196.75.121.112:443;     (動態(tài)生成)
}

server {
    listen       443 ssl; (動態(tài)生成)
    server_name  www.test.com; (動態(tài)生成)
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;; 
    ssl_certificate             /home/build/openresty/nginx/cert/dealssl/www.bestenover.com.crt; (動態(tài)生成)

    location / { 
        proxy_pass https://www_test; (動態(tài)生成)
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto https;
        proxy_redirect off;
    }   
}

要實現(xiàn)動態(tài)配置首先要把核心信息存儲到etcd中

func TestNginxMkdir(t *testing.T) {
    mkdir(kv, "/nginx")
    mkdir(kv, "/nginx/https")
    mkdir(kv, "/nginx/http")
    mkdir(kv, "/nginx/ssl")
    mkdir(kv, "/nginx/https/www")
    mkdir(kv, "/nginx/https/www/server")
    mkdir(kv, "/nginx/https/www/upstream")
    mkdir(kv, "/nginx/https/www/server/location")
}

confd注冊監(jiān)控etcdkey/nginx/窖维,只要發(fā)生變化就通知confd根據(jù)模板生成配置榆综。confd默認的配置路徑為/etc/confd/,創(chuàng)建conf.dtemplate兩個目錄铸史,分別存放配置資源和配置模板鼻疮。

nginx的配置資源如下所示:test.conf.toml

[template]
src = "test.conf.tmpl"
dest = "/tmp/test.conf"
keys = [
    "/nginx",
]
check_cmd = "echo a"
reload_cmd = "echo b"

nginx的配置模板如下所示:test.conf.tmpl

upstream www_{{getv "/nginx/https/www/server/server_name"}} {
    {{range getvs "/nginx/https/www/upstream/*"}}server {{.}};
    {{end}}
}

server {
    server_name         {{getv "/nginx/https/www/server/server_name"}}:443;
    ssl on
    ssl_certificate     {{getv "/nginx/https/www/server/ssl_certificate"}};
    ssl_certificate_key {{getv "/nginx/https/www/server/ssl_certificate_key"}};
    location / {
        proxy_pass        http://www_{{getv "/nginx/https/www/server/server_name"}};
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto https;
        proxy_redirect    off;
    }
}

開啟confd : confd -watch -backend etcd -node http://127.0.0.1:2379
設(shè)置內(nèi)容

func TestNginxSetVal(t *testing.T) {
    setVal(kv, "/nginx/https/www/server/server_name", "test.com")
    setVal(kv, "/nginx/https/www/server/ssl_certificate", "client.crt")
    setVal(kv, "/nginx/https/www/server/ssl_certificate_key", "client.key")
    setVal(kv, "/nginx/https/www/upstream/server1", "192.168.4.2:443")
    setVal(kv, "/nginx/https/www/upstream/server2", "192.168.5.2:443")
}

生成結(jié)果

  1 upstream www_test.com {                                                                                                                                                                                         
  2     server 192.168.4.2:443;
  3     server 192.168.5.2:443;
  4 
  5 }
  6 
  7 server {
  8     server_name         test.com:443;
  9     ssl on
 10     ssl_certificate     client.crt;
 11     ssl_certificate_key client.key;
 12     location / {
 13         proxy_pass        http://www_test.com;
 14         proxy_set_header Host $host;
 15         proxy_set_header X-Real-IP $remote_addr;
 16         proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
 17         proxy_set_header X-Forwarded-Proto https;
 18         proxy_redirect    off;
 19     }
 20 }
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市琳轿,隨后出現(xiàn)的幾起案子判沟,更是在濱河造成了極大的恐慌,老刑警劉巖崭篡,帶你破解...
    沈念sama閱讀 206,723評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件挪哄,死亡現(xiàn)場離奇詭異,居然都是意外死亡琉闪,警方通過查閱死者的電腦和手機迹炼,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,485評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來颠毙,“玉大人斯入,你說我怎么就攤上這事≈郏” “怎么了刻两?”我有些...
    開封第一講書人閱讀 152,998評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長涵防。 經(jīng)常有香客問我闹伪,道長,這世上最難降的妖魔是什么壮池? 我笑而不...
    開封第一講書人閱讀 55,323評論 1 279
  • 正文 為了忘掉前任偏瓤,我火速辦了婚禮,結(jié)果婚禮上椰憋,老公的妹妹穿的比我還像新娘厅克。我一直安慰自己,他們只是感情好橙依,可當我...
    茶點故事閱讀 64,355評論 5 374
  • 文/花漫 我一把揭開白布证舟。 她就那樣靜靜地躺著,像睡著了一般窗骑。 火紅的嫁衣襯著肌膚如雪女责。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,079評論 1 285
  • 那天创译,我揣著相機與錄音抵知,去河邊找鬼。 笑死软族,一個胖子當著我的面吹牛刷喜,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播立砸,決...
    沈念sama閱讀 38,389評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼掖疮,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了颗祝?” 一聲冷哼從身側(cè)響起浊闪,我...
    開封第一講書人閱讀 37,019評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎螺戳,沒想到半個月后规揪,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,519評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡温峭,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,971評論 2 325
  • 正文 我和宋清朗相戀三年猛铅,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片凤藏。...
    茶點故事閱讀 38,100評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡奸忽,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出揖庄,到底是詐尸還是另有隱情栗菜,我是刑警寧澤,帶...
    沈念sama閱讀 33,738評論 4 324
  • 正文 年R本政府宣布蹄梢,位于F島的核電站疙筹,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜而咆,卻給世界環(huán)境...
    茶點故事閱讀 39,293評論 3 307
  • 文/蒙蒙 一霍比、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧暴备,春花似錦悠瞬、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,289評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至障癌,卻和暖如春凌外,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背涛浙。 一陣腳步聲響...
    開封第一講書人閱讀 31,517評論 1 262
  • 我被黑心中介騙來泰國打工趴乡, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人蝗拿。 一個月前我還...
    沈念sama閱讀 45,547評論 2 354
  • 正文 我出身青樓晾捏,卻偏偏與公主長得像,于是被迫代替她去往敵國和親哀托。 傳聞我的和親對象是個殘疾皇子惦辛,可洞房花燭夜當晚...
    茶點故事閱讀 42,834評論 2 345

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