轉(zhuǎn)載:Etcd超全解:原理闡釋及部署設(shè)置的最佳實(shí)踐

轉(zhuǎn)載:Etcd超全解:原理闡釋及部署設(shè)置的最佳實(shí)踐

介? 紹

Etcd是一個(gè)開源的分布式鍵值存儲(chǔ),它由CoreOS團(tuán)隊(duì)開發(fā),現(xiàn)在由Cloud Native Computing Foundation負(fù)責(zé)管理行贪。這個(gè)詞的發(fā)音是“et-cee-dee”骄瓣,表示在多臺(tái)機(jī)器上分發(fā)Unix系統(tǒng)的“/etc”目錄求泰,其中包含了大量的全局配置文件。它是許多分布式系統(tǒng)的主干涩惑,為跨服務(wù)器集群存儲(chǔ)數(shù)據(jù)提供可靠的方式。它適用于各種操作系統(tǒng)桑驱,包括Linux竭恬、BSD和OS X。

Etcd具有下面這些屬性:

完全復(fù)制:集群中的每個(gè)節(jié)點(diǎn)都可以使用完整的存檔

高可用性:Etcd可用于避免硬件的單點(diǎn)故障或網(wǎng)絡(luò)問題

一致性:每次讀取都會(huì)返回跨多主機(jī)的最新寫入

簡(jiǎn)單:包括一個(gè)定義良好熬的、面向用戶的API(gRPC)

安全:實(shí)現(xiàn)了帶有可選的客戶端證書身份驗(yàn)證的自動(dòng)化TLS

快速:每秒10000次寫入的基準(zhǔn)速度

可靠:使用Raft算法實(shí)現(xiàn)了存儲(chǔ)的合理分布

Etcd的工作原理

在理解Etcd的工作機(jī)制之前痊硕,我們先定義三個(gè)關(guān)鍵概念:leaders、elections以及terms押框。在一個(gè)基于Raft的系統(tǒng)中岔绸,集群使用election為給定的term選擇leader。

Leader處理所有需要集群一致協(xié)商的客戶端請(qǐng)求橡伞。不需要一致協(xié)商的請(qǐng)求(如讀群腥唷)可以由任何集群成員處理。Leader負(fù)責(zé)接受新的更改兑徘,將信息復(fù)制到follower節(jié)點(diǎn)刚盈,并在follower驗(yàn)證接受后提交更改。每個(gè)集群在任何給定的時(shí)間內(nèi)只能有一個(gè)leader挂脑。

如果leader掛了或者不再響應(yīng)了藕漱,那么其他節(jié)點(diǎn)將在預(yù)定的時(shí)間超時(shí)之后開啟一個(gè)新的term來(lái)創(chuàng)建新election。每個(gè)節(jié)點(diǎn)維護(hù)一個(gè)隨機(jī)的election計(jì)時(shí)器最域,該計(jì)時(shí)器表示節(jié)點(diǎn)在調(diào)用新的election以及選擇自己作為候選之前需要等待的時(shí)間谴分。

如果節(jié)點(diǎn)在超時(shí)發(fā)生之前沒有收到leader的消息,則該節(jié)點(diǎn)將通過啟動(dòng)新的term镀脂、將自己標(biāo)記為候選牺蹄,并要求其他節(jié)點(diǎn)投票來(lái)開始新的election。每個(gè)節(jié)點(diǎn)投票給請(qǐng)求其投票的第一個(gè)候選薄翅。如果候選從集群中的大多數(shù)節(jié)點(diǎn)處獲得了選票沙兰,那么它就成為了新的leader氓奈。但是,如果存在多個(gè)候選且獲得了相同數(shù)量的選票鼎天,那么現(xiàn)有的election term將在沒有l(wèi)eader的情況下結(jié)束舀奶,而新的term將以新的隨機(jī)選舉計(jì)時(shí)器開始。

如上所述斋射,任何更改都必須連接到leader節(jié)點(diǎn)育勺。Etcd沒有立即接受和提交更改,而是使用Raft算法確保大多數(shù)節(jié)點(diǎn)都同意更改罗岖。Leader將提議的新值發(fā)送到集群中的每個(gè)節(jié)點(diǎn)涧至。然后,節(jié)點(diǎn)發(fā)送一條消息確認(rèn)收到了新值桑包。如果大多數(shù)節(jié)點(diǎn)確認(rèn)接收南蓬,那么leader提交新值,并向每個(gè)節(jié)點(diǎn)發(fā)送將該值提交到日志的消息哑了。這意味著每次更改都需要得到集群節(jié)點(diǎn)的仲裁才能提交赘方。

Kubernetes中的Etcd

自從2014年成為Kubernetes的一部分以來(lái),Etcd社區(qū)呈現(xiàn)指數(shù)級(jí)的增長(zhǎng)弱左。CoreOS窄陡、谷歌、Redhat科贬、IBM泳梆、思科、華為等等均是Etcd的貢獻(xiàn)成員榜掌。其中AWS优妙、谷歌云平臺(tái)和Azure等大型云提供商成功在生產(chǎn)環(huán)境中使用了Etcd。

Etcd在Kubernetes中的工作是為分布式系統(tǒng)安全存儲(chǔ)關(guān)鍵數(shù)據(jù)憎账。它最著名的是Kubernetes的主數(shù)據(jù)存儲(chǔ)套硼,用于存儲(chǔ)配置數(shù)據(jù)、狀態(tài)和元數(shù)據(jù)胞皱。由于Kubernetes通常運(yùn)行在幾臺(tái)機(jī)器的集群上邪意,因此它是一個(gè)分布式系統(tǒng),需要Etcd這樣的分布式數(shù)據(jù)存儲(chǔ)反砌。

Etcd使得跨集群存儲(chǔ)數(shù)據(jù)和監(jiān)控更改變得更加容易雾鬼,它允許來(lái)自Kubernetes集群的任何節(jié)點(diǎn)讀取和寫入數(shù)據(jù)。Kubernetes使用Etcd的watch功能來(lái)監(jiān)控系統(tǒng)實(shí)際(actual)狀態(tài)或期望(desired)狀態(tài)的變化宴树。如果這兩個(gè)狀態(tài)不同策菜,Kubernetes會(huì)做出一些改變來(lái)調(diào)和這兩個(gè)狀態(tài)。kubectl命令的每次讀取都從Etcd存儲(chǔ)的數(shù)據(jù)中檢索,所做的任何更改(kubectl apply)都會(huì)在Etcd中創(chuàng)建或更新條目又憨,每次崩潰都會(huì)觸發(fā)etcd中值的修改翠霍。

部署以及硬件建議

出于測(cè)試或開發(fā)目的,Etcd可以在筆記本電腦或輕量云上運(yùn)行蠢莺。然而寒匙,在生產(chǎn)環(huán)境中運(yùn)行Etcd集群時(shí),我們應(yīng)該考慮Etcd官方文檔提供的指導(dǎo)躏将。它為良好穩(wěn)定的生產(chǎn)部署提供了一個(gè)良好的起點(diǎn)锄弱。需要留意的是:

Etcd會(huì)將數(shù)據(jù)寫入磁盤,因此強(qiáng)烈推薦使用SSD

始終使用奇數(shù)個(gè)集群數(shù)量耸携,因?yàn)樾枰ㄟ^仲裁來(lái)更新集群的狀態(tài)

出于性能考慮棵癣,集群通常不超過7個(gè)節(jié)點(diǎn)

讓我們回顧一下在Kubernetes中部署Etcd集群所需的步驟。之后夺衍,我們將演示一些基本的CLI命令以及API調(diào)用。我們將結(jié)合Kubernetes的概念(如StatefulSets和PersistentVolume)進(jìn)行部署喜命。

預(yù)先準(zhǔn)備

在繼續(xù)demo之前沟沙,我們需要準(zhǔn)備:

一個(gè)谷歌云平臺(tái)的賬號(hào):免費(fèi)的tier應(yīng)該足夠了。你也可以選擇大多數(shù)其他云提供商壁榕,只需進(jìn)行少量修改即可矛紫。

一個(gè)運(yùn)行Rancher的服務(wù)器

啟動(dòng)Rancher實(shí)例

在你控制的服務(wù)器上啟動(dòng)Rancher實(shí)例。這里有一個(gè)非常簡(jiǎn)單直觀的入門指南:https://rancher.com/quick-start/

使用Rancher部署GKE集群

參照本指南使用Rancher在GCP賬戶中設(shè)置和配置Kubernetes集群:

https://rancher.com/docs/rancher/v2.x/en/cluster-provisioning/hosted-kubernetes-clusters/gke/

在運(yùn)行Rancher實(shí)例的同一服務(wù)器上安裝Google Cloud SDK以及kubelet命令牌里。按照上面提供的鏈接安裝SDK颊咬,并通過Rancher UI安裝kubelet。

使用gcloud init和gcloud auth login牡辽,確保gcloud命令能夠訪問你的GCP賬戶喳篇。

集群部署后,輸入下面的命令檢查基本的kubectl功能:

在部署Etcd集群(通過kubectl或在Rancher的UI中導(dǎo)入YAML文件)之前态辛,我們需要配置一些項(xiàng)麸澜。在GCE中,默認(rèn)的持久化磁盤是pd-standard奏黑。我們將為Etcd部署配置pd-ssd炊邦。這不是強(qiáng)制性的,不過根據(jù)Etcd的建議熟史,SSD是非常好的選擇馁害。查看此鏈接可以了解其他云提供商的存儲(chǔ)類:

https://kubernetes.io/docs/concepts/storage/storage-classes/


讓我們檢查一下GCE提供的可用存儲(chǔ)類。正如預(yù)期的那樣蹂匹,我們看到了一個(gè)默認(rèn)的結(jié)果碘菜,叫做standard:

應(yīng)用下面這個(gè)YAML文件,更新zone的值來(lái)匹配你的首選項(xiàng),這樣我們就可以使用SSD存儲(chǔ)了:

我們?cè)僖淮螜z查炉媒,可以看到踪区,除了默認(rèn)standard類之外,ssd也可以使用了:

現(xiàn)在我們可以繼續(xù)部署Etcd集群了吊骤。我們將創(chuàng)建一個(gè)帶有3個(gè)副本的StatefulSet缎岗,每個(gè)副本都有一個(gè)ssd storageClass的專用卷。我們還需要部署兩個(gè)服務(wù)白粉,一個(gè)用于內(nèi)部集群通信传泊,一個(gè)用于通過API從外部訪問集群。

在搭建集群時(shí)鸭巴,我們需要將一些參數(shù)傳遞給Etcd二進(jìn)制文件再到數(shù)據(jù)存儲(chǔ)中眷细。Listen-client-urls和listen-peer-urls選項(xiàng)指定Etcd服務(wù)器用于接受傳入連接的本地地址。指定0.0.0.0作為IP地址意味著Etcd將監(jiān)聽所有可用接口上的連接鹃祖。Advertise-client-urls和initial-advertise-peer-urls參數(shù)指定了在Etcd客戶端或者其他Etcd成員聯(lián)系etcd服務(wù)器時(shí)應(yīng)該使用的地址溪椎。

下面的YAML文件定義了我們的兩個(gè)服務(wù)以及Etcd StatefulSe圖:

# etcd-sts.yaml---apiVersion: v1kind: Servicemetadata: ?name: etcd-clientspec: ?type: LoadBalancer ?ports: ?- name: etcd-client ? ?port:2379protocol: TCP ? ?targetPort:2379selector: ? ?app: etcd---apiVersion: v1kind: Servicemetadata: ?name: etcdspec: ?clusterIP: None ?ports: ?- port:2379name: client ?- port:2380name: peer ?selector: ? ?app: etcd---apiVersion: apps/v1beta1kind: StatefulSetmetadata: ?name: etcd ?labels: ? ?app: etcdspec: ?serviceName: etcd ?replicas:3template: ? ?metadata: ? ? ?name: etcd ? ? ?labels: ? ? ? ?app: etcd ? ?spec: ? ? ?containers: ? ? ?- name: etcd ? ? ? ?image: quay.io/coreos/etcd:latest ? ? ? ?ports: ? ? ? ?- containerPort:2379name: client ? ? ? ?- containerPort:2380name: peer ? ? ? ?volumeMounts: ? ? ? ?- name: data ? ? ? ? ?mountPath: /var/run/etcd ? ? ? ?command: ? ? ? ? ?- /bin/sh ? ? ? ? ?- -c ? ? ? ? ?-| ? ? ? ? ? ?PEERS="etcd-0=http://etcd-0.etcd:2380,etcd-1=http://etcd-1.etcd:2380,etcd-2=http://etcd-2.etcd:2380"exec etcd --name ${HOSTNAME} \ ? ? ? ? ? ? ?--listen-peer-urls http://0.0.0.0:2380\ ? ? ? ? ? ? ?--listen-client-urls http://0.0.0.0:2379\ ? ? ? ? ? ? ?--advertise-client-urls http://${HOSTNAME}.etcd:2379\ ? ? ? ? ? ? ?--initial-advertise-peer-urls http://${HOSTNAME}:2380\ ? ? ? ? ? ? ?--initial-cluster-token etcd-cluster-1\ ? ? ? ? ? ? ?--initial-cluster ${PEERS} \ ? ? ? ? ? ? ?--initial-cluster-state new \ ? ? ? ? ? ? ?--data-dir /var/run/etcd/default.etcd ?volumeClaimTemplates: ?- metadata: ? ? ?name: data ? ?spec: ? ? ?storageClassName: ssd ? ? ?accessModes: ["ReadWriteOnce"] ? ? ?resources: ? ? ? ?requests: ? ? ? ? ?storage: 1Gi

輸入下列命令應(yīng)用YAML:

在應(yīng)用YAML文件后,我們可以在Rancher提供的不同選項(xiàng)卡中定義資源:

與Etcd交互

與Etcd交互的方式主要有兩種:使用etcdctl命令或者直接通過RESTful API恬口。我們將簡(jiǎn)要介紹這兩種方法校读,不過你還可以通過訪問這里和這里的完整文檔找到更加深入的信息和示例。

Etcdctl是一個(gè)和Etcd服務(wù)器交互的命令行接口祖能。它可以用于執(zhí)行各種操作歉秫,如設(shè)置、更新或者刪除鍵养铸、驗(yàn)證集群健康情況遗淳、添加或刪除Etcd節(jié)點(diǎn)以及生成數(shù)據(jù)庫(kù)快照沐祷。默認(rèn)情況下讥此,etcdctl使用v2 API與Etcd服務(wù)器通信來(lái)獲得向后兼容性绸狐。如果希望etcdctl使用v3 API和Etcd通信,則必須通過ETCDCTL_API環(huán)境變量將版本設(shè)置為3筛圆。

對(duì)于API裂明,發(fā)送到Etcd服務(wù)器的每一個(gè)請(qǐng)求都是一個(gè)gRPC遠(yuǎn)程過程調(diào)用。這個(gè)gRPC網(wǎng)關(guān)提供一個(gè)RESTful代理太援,能夠?qū)TTP/JSON請(qǐng)求轉(zhuǎn)換為gRPC消息闽晦。

讓我們來(lái)找到API調(diào)用所需的外部IP:

我們應(yīng)該還能找到3個(gè)pods的名稱,這樣我們就可以使用etcdctl命令:

我們檢查Etcd版本提岔。為此我們可以使用API或CLI(v2和v3).根據(jù)你選擇的方法仙蛉, 輸出的結(jié)果將略有不同。

使用此命令可直接與API聯(lián)系:

檢查API版本為v2的etcdctl客戶端碱蒙,輸入:

檢查API版本為v3的etcdctl客戶端荠瘪,則輸入:

接下來(lái)夯巷,列出集群成員,就像我們上面做的那樣:

{"members":[{"id":"2e80f96756a54ca9","name":"etcd-0","peerURLs":["http://etcd-0.etcd:2380"],"clientURLs":["http://etcd-0.etcd:2379"]},{"id":"7fd61f3f79d97779","name":"etcd-1","peerURLs":["http://etcd-1.etcd:2380"],"clientURLs":["http://etcd-1.etcd:2379"]},{"id":"b429c86e3cd4e077","name":"etcd-2","peerURLs":["http://etcd-2.etcd:2380"],"clientURLs":["http://etcd-2.etcd:2379"]}]}

V2版本的etcdctl:

V3版本的etcdctl:

在Etcd中設(shè)置和檢索值

下面我們將介紹的最后一個(gè)示例是在Etcd集群中全部3個(gè)pods上創(chuàng)建一個(gè)鍵并檢查其值哀墓。然后我們會(huì)殺掉leader趁餐,在我們的場(chǎng)景中是etcd-0,然后來(lái)看看新的leader是如何選出來(lái)的篮绰。最后后雷,在集群恢復(fù)之后,我們將在所有成員上驗(yàn)證之前創(chuàng)建的鍵的值吠各。我們會(huì)看到臀突,沒有數(shù)據(jù)丟失的情況發(fā)生,集群只是換了一個(gè)leader而已贾漏。

我們可以通過輸入下面的命令來(lái)驗(yàn)證集群最初是健康的:

接下來(lái)候学,驗(yàn)證當(dāng)前l(fā)eader。最后一個(gè)字段表明etcd-0是我們集群中的leader:

使用該API纵散,我們將創(chuàng)建一個(gè)名為message的鍵并給它分配一個(gè)值梳码,請(qǐng)記住在下面的命令中把IP地址替換為你在集群中通過下面命令獲取到的地址:

無(wú)論查詢哪個(gè)成員,鍵都具有相同的值伍掀。這幫助我們驗(yàn)證值是否已經(jīng)復(fù)制到其他節(jié)點(diǎn)并提交到日志边翁。

演示高可用性和恢復(fù)

接下來(lái),我們可以殺掉Etcd集群leader硕盹。這樣我們可以看到新的leader是如何選出的,以及集群如何從degraded狀態(tài)中恢復(fù)過來(lái)叨咖。刪除與上面發(fā)現(xiàn)的Etcd leader相關(guān)的pod:

下面我們檢查一下集群的健康情況:

failed to check the health of member 2e80f96756a54ca9 on http://etcd-0.etcd:2379: Get http://etcd-0.etcd:2379/health: dial tcp: lookup etcd-0.etcd on 10.15.240.10:53: no such host?

member 2e80f96756a54ca9 is unreachable: [http://etcd-0.etcd:2379] are all unreachable?

member 7fd61f3f79d97779 is healthy: got healthy result from http://etcd-1.etcd:2379?

member b429c86e3cd4e077 is healthy: got healthy result from http://etcd-2.etcd:2379cluster is degraded?

command terminated with exit code 5

上面的信息表明瘩例,由于失去了leader節(jié)點(diǎn),集群出于degrade狀態(tài)甸各。

一旦Kubernetes通過啟動(dòng)新實(shí)例來(lái)響應(yīng)刪除的pod垛贤,Etcd集群應(yīng)該就恢復(fù)過來(lái)了:

輸入下面指令,我們可以看到新的leader已經(jīng)選出來(lái)了:

在我們的例子中趣倾,etcd-1節(jié)點(diǎn)被選為leader

如果我們?cè)僖淮螜z查message鍵的值聘惦,會(huì)發(fā)現(xiàn)沒有出現(xiàn)數(shù)據(jù)的損失:

結(jié)? 論

Etcd是一種非常強(qiáng)大、高可用以及可靠的分布式鍵值存儲(chǔ)儒恋,專門為特定用例設(shè)計(jì)善绎。常見的例子包括存儲(chǔ)數(shù)據(jù)哭連接細(xì)節(jié)、緩存設(shè)置诫尽、特性標(biāo)記等等禀酱。它被設(shè)計(jì)成順序一致的,因此在整個(gè)集群中每個(gè)事件都是以相同的順序存儲(chǔ)牧嫉。

我們了解了如何在Rancher的幫助下用Kubernetes建立并運(yùn)行etcd集群剂跟。之后减途,我們能夠使用一些基本的Etcd命令進(jìn)行操作。為了更好的了解這個(gè)項(xiàng)目曹洽,鍵是如何組織的鳍置,如何為鍵設(shè)置TTLs,或者如何備份所有數(shù)據(jù)送淆,參考官方的Etcd repo會(huì)是個(gè)不錯(cuò)的選擇:

https://github.com/etcd-io/etcd/tree/master/Documentation

推薦閱讀

健康檢查詳解:機(jī)制税产、配置、對(duì)比坊夫、實(shí)操

Kubernetes集群監(jiān)控詳解

在Kubernetes中部署Elasticsearch

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末砖第,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子环凿,更是在濱河造成了極大的恐慌梧兼,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,941評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件智听,死亡現(xiàn)場(chǎng)離奇詭異羽杰,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)到推,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門考赛,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人莉测,你說(shuō)我怎么就攤上這事颜骤。” “怎么了捣卤?”我有些...
    開封第一講書人閱讀 165,345評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵忍抽,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我董朝,道長(zhǎng)鸠项,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,851評(píng)論 1 295
  • 正文 為了忘掉前任子姜,我火速辦了婚禮祟绊,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘哥捕。我一直安慰自己牧抽,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,868評(píng)論 6 392
  • 文/花漫 我一把揭開白布扭弧。 她就那樣靜靜地躺著阎姥,像睡著了一般。 火紅的嫁衣襯著肌膚如雪鸽捻。 梳的紋絲不亂的頭發(fā)上呼巴,一...
    開封第一講書人閱讀 51,688評(píng)論 1 305
  • 那天泽腮,我揣著相機(jī)與錄音,去河邊找鬼衣赶。 笑死诊赊,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的府瞄。 我是一名探鬼主播碧磅,決...
    沈念sama閱讀 40,414評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼遵馆!你這毒婦竟也來(lái)了鲸郊?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,319評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤货邓,失蹤者是張志新(化名)和其女友劉穎秆撮,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體换况,經(jīng)...
    沈念sama閱讀 45,775評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡职辨,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了戈二。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片舒裤。...
    茶點(diǎn)故事閱讀 40,096評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖觉吭,靈堂內(nèi)的尸體忽然破棺而出腾供,到底是詐尸還是另有隱情,我是刑警寧澤鲜滩,帶...
    沈念sama閱讀 35,789評(píng)論 5 346
  • 正文 年R本政府宣布台腥,位于F島的核電站,受9級(jí)特大地震影響绒北,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜察署,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,437評(píng)論 3 331
  • 文/蒙蒙 一闷游、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧贴汪,春花似錦脐往、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至阳懂,卻和暖如春梅尤,著一層夾襖步出監(jiān)牢的瞬間柜思,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評(píng)論 1 271
  • 我被黑心中介騙來(lái)泰國(guó)打工巷燥, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留赡盘,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,308評(píng)論 3 372
  • 正文 我出身青樓缰揪,卻偏偏與公主長(zhǎng)得像陨享,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子钝腺,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,037評(píng)論 2 355

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